Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up async code #631

Merged
merged 31 commits into from
Dec 6, 2024
Merged
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fe9931a
Fix asynchronous commands
fyellin Nov 1, 2024
c733c5f
Fix lint problems and codegen problems.
fyellin Nov 1, 2024
f8a71e6
Remove assertion.
fyellin Nov 2, 2024
fbfccb7
Merge branch 'main' into async_handler
fyellin Nov 4, 2024
bebac48
_api.GPU should be the owner of its internal representation.
fyellin Nov 4, 2024
3ae7780
Thank you, ruff!
fyellin Nov 4, 2024
5fb6502
For now, use anyio
fyellin Nov 5, 2024
780ff9b
Continue to find the minimum changes
fyellin Nov 5, 2024
f831e6d
Continue to find the minimum changes
fyellin Nov 5, 2024
3b024be
Merge branch 'main' into async_handler
fyellin Nov 5, 2024
81cf2f7
Allow "await WgpuAwaitable(..)"
fyellin Nov 6, 2024
b08dc8b
Fix ruff format
fyellin Nov 6, 2024
84db283
Attempt to delay installing anyio
fyellin Nov 6, 2024
16e082e
Attempt to delay installing anyio
fyellin Nov 6, 2024
3eb1a59
Add another test.
fyellin Nov 7, 2024
466d3d9
Merge remote-tracking branch 'origin/main' into async_handler
fyellin Nov 7, 2024
54488dc
Changes requested by reviewers
fyellin Nov 7, 2024
45361ce
Change sleep to 0
fyellin Nov 14, 2024
1b903fa
Merge branch 'main' into async_handler
fyellin Nov 19, 2024
cd574c8
Small tweaks/cleanup
almarklein Nov 20, 2024
bf5862c
Implement new_array to prevent premature substruct cg
almarklein Nov 20, 2024
2f150c1
add little debug function that was very useful
almarklein Nov 20, 2024
b207bb7
fix import in test script
almarklein Nov 20, 2024
b1022ed
codegen
almarklein Nov 20, 2024
eedd8c9
Merge branch 'main' into async_handler
fyellin Nov 21, 2024
f650208
Make array store sub-refs, not sub-elements
almarklein Nov 21, 2024
c4d2388
use sniffio instead of anyio
almarklein Dec 6, 2024
9faec92
Merge branch 'main' into async_handler
almarklein Dec 6, 2024
7e6e87e
at least one sleep
almarklein Dec 6, 2024
f5acb60
fix new_array
almarklein Dec 6, 2024
6975af0
fix wheel test
almarklein Dec 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
For now, use anyio
fyellin committed Nov 5, 2024
commit 5fb6502d263cf70a8015a3716e0d8ed4bd2024b8
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ license = { file = "LICENSE" }
authors = [{ name = "Almar Klein" }, { name = "Korijn van Golen" }]
keywords = ["webgpu", "wgpu", "vulkan", "metal", "DX12", "opengl"]
requires-python = ">= 3.9"
dependencies = ["cffi>=1.15.0", "rubicon-objc>=0.4.1; sys_platform == 'darwin'"]
dependencies = ["cffi>=1.15.0", "rubicon-objc>=0.4.1; sys_platform == 'darwin'", "anyio>=4.6"]

[project.optional-dependencies]
# For users
45 changes: 28 additions & 17 deletions tests/test_async.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import asyncio
import anyio

import pytest

@@ -8,7 +8,7 @@
from wgpu.backends.wgpu_native import WgpuAwaitable


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.parametrize("use_async", [False, True])
async def test_awaitable_async(use_async):
count = 0
@@ -28,20 +28,20 @@ def poll_function():
awaitable = WgpuAwaitable("test", callback, finalizer, poll_function)

if use_async:
result = await awaitable
result = await awaitable.async_wait()
else:
result = awaitable.sync_wait()
assert result == 10 * 10


@pytest.mark.asyncio
@pytest.mark.anyio
async def test_asynchronous_get_device():
adapter = await wgpu.gpu.request_adapter_async(power_preference="high-performance")
device = await adapter.request_device_async()
assert device is not None


@pytest.mark.asyncio
@pytest.mark.anyio
async def test_asynchronous_buffer_map():
device = wgpu.utils.get_default_device()

@@ -63,7 +63,7 @@ async def test_asynchronous_buffer_map():
assert bytes(data2) == data


@pytest.mark.asyncio
@pytest.mark.anyio
async def test_asynchronous_make_pipeline():
device = wgpu.utils.get_default_device()

@@ -79,17 +79,28 @@ async def test_asynchronous_make_pipeline():

shader = device.create_shader_module(code=shader_source)

compute_pipeline, render_pipeline = await asyncio.gather(
device.create_compute_pipeline_async(layout="auto", compute={"module": shader}),
device.create_render_pipeline_async(
layout="auto",
vertex={
"module": shader,
},
depth_stencil={"format": TextureFormat.rgba8unorm},
),
)

results = [None, None]
async with anyio.create_task_group() as tg:
# It's unfortunate anyio doesn't have async.gather. This code would just be
# compute_pipeline, render_pipeline = asyncio.gather(.....)
async def create_compute_pipeline():
results[0] = await device.create_compute_pipeline_async(
layout="auto", compute={"module": shader}
)

async def create_render_pipeline():
results[1] = await device.create_render_pipeline_async(
layout="auto",
vertex={
"module": shader,
},
depth_stencil={"format": TextureFormat.rgba8unorm},
)

tg.start_soon(create_compute_pipeline)
tg.start_soon(create_render_pipeline)

compute_pipeline, render_pipeline = results
assert compute_pipeline is not None
assert render_pipeline is not None

12 changes: 6 additions & 6 deletions wgpu/backends/wgpu_native/_api.py
Original file line number Diff line number Diff line change
@@ -357,7 +357,7 @@ async def request_adapter_async(
force_fallback_adapter=force_fallback_adapter,
canvas=canvas,
) # no-cover
return await awaitable
return await awaitable.async_wait()

def _request_adapter(
self, *, power_preference=None, force_fallback_adapter=False, canvas=None
@@ -873,7 +873,7 @@ async def request_device_async(
)
# Note that although we claim this function is async, the callback always
# happens inside the call to libf.wgpuAdapterRequestDevice
return await awaitable
return await awaitable.async_wait()

def _request_device(
self,
@@ -1602,7 +1602,7 @@ def finalizer(id):
self._internal, descriptor, callback, ffi.NULL
)

return await awaitable
return await awaitable.async_wait()

def _create_compute_pipeline_descriptor(
self,
@@ -1703,7 +1703,7 @@ def finalizer(id):
self._internal, descriptor, callback, ffi.NULL
)

return await awaitable
return await awaitable.async_wait()

def _create_render_pipeline_descriptor(
self,
@@ -2079,7 +2079,7 @@ async def map_async(
self, mode: flags.MapMode, offset: int = 0, size: Optional[int] = None
):
awaitable = self._map(mode, offset, size) # for now
return await awaitable
return await awaitable.async_wait()

def _map(self, mode, offset=0, size=None):
sync_on_read = True
@@ -3539,7 +3539,7 @@ def on_submitted_work_done_sync(self):

async def on_submitted_work_done_async(self):
awaitable = self._on_submitted_word_done()
await awaitable
await awaitable.async_wait()

def _on_submitted_word_done(self):
@ffi.callback("void(WGPUQueueWorkDoneStatus, void*)")
10 changes: 8 additions & 2 deletions wgpu/backends/wgpu_native/_helpers.py
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@
import time
from queue import deque

import anyio

from ._ffi import ffi, lib, lib_path
from ..._diagnostics import DiagnosticsBase
from ...classes import (
@@ -241,13 +243,16 @@ def __init__(self, title, callback, finalizer, poll_function=None, timeout=5.0):
self.finalizer = finalizer # function to finish the result
self.poll_function = poll_function # call this to poll wgpu
self.maxtime = time.perf_counter() + float(timeout)
self.event = anyio.Event()
self.result = None

def set_result(self, result):
self.result = (result, None)
self.event.set()

def set_error(self, error):
self.result = (None, error)
self.event.set()

def sync_wait(self):
if not self.poll_function:
@@ -258,13 +263,14 @@ def sync_wait(self):
time.sleep(self.SLEEP_TIME)
return self.finish()

def __await__(self):
async def async_wait(self):
if not self.poll_function:
if self.result is None:
raise RuntimeError("Expected callback to have already happened")
else:
while not self._is_done():
yield None
with anyio.move_on_after(self.SLEEP_TIME):
self.event.wait()
return self.finish()

def _is_done(self):