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

Tweaks to examples #610

Merged
merged 2 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
105 changes: 73 additions & 32 deletions examples/cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,47 +19,43 @@
# %% Entrypoints (sync and async)


def setup_cube(canvas, power_preference="high-performance", limits=None):
"""Regular function to setup a viz on the given canvas."""
def setup_drawing_sync(canvas, power_preference="high-performance", limits=None):
"""Setup to draw a rotating cube on the given canvas.

The given canvas must implement WgpuCanvasInterface, but nothing more.
Returns the draw function.
"""

adapter = wgpu.gpu.request_adapter_sync(power_preference=power_preference)
device = adapter.request_device_sync(required_limits=limits)

pipeline_layout, uniform_buffer, bind_groups = create_pipeline_layout(device)
pipeline_kwargs = get_render_pipeline_kwargs(canvas, device, pipeline_layout)

render_pipeline = device.create_render_pipeline(**pipeline_kwargs)

render_pipeline = get_render_pipeline_sync(canvas, device, pipeline_layout)
draw_function = get_draw_function(
canvas, device, render_pipeline, uniform_buffer, bind_groups
return get_draw_function(
canvas, device, render_pipeline, uniform_buffer, bind_groups, asynchronous=False
)

canvas.request_draw(draw_function)

async def setup_drawing_async(canvas, limits=None):
"""Setup to async-draw a rotating cube on the given canvas.

async def setup_cube_async(canvas, limits=None):
"""Async function to setup a viz on the given canvas."""
The given canvas must implement WgpuCanvasInterface, but nothing more.
Returns the draw function.
"""

adapter = await wgpu.gpu.request_adapter_async(power_preference="high-performance")
device = await adapter.request_device_async(required_limits=limits)

pipeline_layout, uniform_buffer, bind_groups = create_pipeline_layout(device)
pipeline_kwargs = get_render_pipeline_kwargs(canvas, device, pipeline_layout)

render_pipeline = await get_render_pipeline_async(canvas, device, pipeline_layout)
draw_function = get_draw_function(
canvas, device, render_pipeline, uniform_buffer, bind_groups
)

canvas.request_draw(draw_function)
render_pipeline = await device.create_render_pipeline_async(**pipeline_kwargs)


def get_render_pipeline_sync(canvas, device, *args):
return device.create_render_pipeline(
**get_render_pipeline_kwargs(canvas, device, *args)
)


async def get_render_pipeline_async(canvas, device, *args):
return await device.create_render_pipeline_async(
**get_render_pipeline_kwargs(canvas, device, *args)
return get_draw_function(
canvas, device, render_pipeline, uniform_buffer, bind_groups, asynchronous=True
)


Expand Down Expand Up @@ -127,6 +123,12 @@ def create_pipeline_layout(device):
usage=wgpu.BufferUsage.UNIFORM | wgpu.BufferUsage.COPY_DST,
)

# Create another buffer to copy data to it (by mapping it and then copying the data)
uniform_buffer.copy_buffer = device.create_buffer(
size=uniform_data.nbytes,
usage=wgpu.BufferUsage.MAP_WRITE | wgpu.BufferUsage.COPY_SRC,
)

# Create texture, and upload data
texture = device.create_texture(
size=texture_size,
Expand Down Expand Up @@ -214,7 +216,9 @@ def create_pipeline_layout(device):
return pipeline_layout, uniform_buffer, bind_groups


def get_draw_function(canvas, device, render_pipeline, uniform_buffer, bind_groups):
def get_draw_function(
canvas, device, render_pipeline, uniform_buffer, bind_groups, *, asynchronous
):
# Create vertex buffer, and upload data
vertex_buffer = device.create_buffer_with_data(
data=vertex_data, usage=wgpu.BufferUsage.VERTEX
Expand All @@ -225,7 +229,7 @@ def get_draw_function(canvas, device, render_pipeline, uniform_buffer, bind_grou
data=index_data, usage=wgpu.BufferUsage.INDEX
)

def draw_frame():
def update_transform():
# Update uniform transform
a1 = -0.3
a2 = time.time()
Expand Down Expand Up @@ -256,17 +260,36 @@ def draw_frame():
)
uniform_data["transform"] = rot2 @ rot1 @ ortho

# Upload the uniform struct
tmp_buffer = device.create_buffer_with_data(
data=uniform_data, usage=wgpu.BufferUsage.COPY_SRC
def upload_uniform_buffer_sync():
if True:
tmp_buffer = uniform_buffer.copy_buffer
tmp_buffer.map_sync(wgpu.MapMode.WRITE)
tmp_buffer.write_mapped(uniform_data)
tmp_buffer.unmap()
else:
tmp_buffer = device.create_buffer_with_data(
data=uniform_data, usage=wgpu.BufferUsage.COPY_SRC
)
command_encoder = device.create_command_encoder()
command_encoder.copy_buffer_to_buffer(
tmp_buffer, 0, uniform_buffer, 0, uniform_data.nbytes
)
device.queue.submit([command_encoder.finish()])

async def upload_uniform_buffer_async():
tmp_buffer = uniform_buffer.copy_buffer
await tmp_buffer.map_async(wgpu.MapMode.WRITE)
tmp_buffer.write_mapped(uniform_data)
tmp_buffer.unmap()
command_encoder = device.create_command_encoder()
command_encoder.copy_buffer_to_buffer(
tmp_buffer, 0, uniform_buffer, 0, uniform_data.nbytes
)
device.queue.submit([command_encoder.finish()])

def draw_frame():
current_texture_view = canvas.get_context().get_current_texture().create_view()
command_encoder = device.create_command_encoder()
render_pass = command_encoder.begin_render_pass(
color_attachments=[
{
Expand All @@ -289,9 +312,20 @@ def draw_frame():

device.queue.submit([command_encoder.finish()])

canvas.request_draw()
def draw_frame_sync():
update_transform()
upload_uniform_buffer_sync()
draw_frame()

async def draw_frame_async():
update_transform()
await upload_uniform_buffer_async()
draw_frame()

return draw_frame
if asynchronous:
return draw_frame_async
else:
return draw_frame_sync


# %% WGSL
Expand Down Expand Up @@ -426,5 +460,12 @@ def draw_frame():

canvas = WgpuCanvas(size=(640, 480), title="wgpu cube example")

setup_cube(canvas)
draw_frame = setup_drawing_sync(canvas)

def animate():
draw_frame()
canvas.request_draw()

canvas.request_draw(animate)

run()
33 changes: 11 additions & 22 deletions examples/gui_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,26 @@

import glfw

from gui_direct import MinimalGlfwCanvas
from triangle import setup_triangle_async # noqa: F401, RUF100
from cube import setup_cube_async # noqa: F401, RUF100
from gui_direct import MinimalGlfwCanvas, poll_glfw_briefly

# from triangle import setup_drawing_async
from cube import setup_drawing_async

async def main_loop():
# create a window with glfw
glfw.init()
# disable automatic API selection, we are not using opengl
glfw.window_hint(glfw.CLIENT_API, glfw.NO_API)
glfw.window_hint(glfw.RESIZABLE, True)
window = glfw.create_window(640, 480, "wgpu with asyncio", None, None)

async def main_loop():
# create canvas
canvas = MinimalGlfwCanvas(window)
# await setup_triangle_async(canvas)
await setup_cube_async(canvas)
canvas = MinimalGlfwCanvas("wgpu gui asyncio")
draw_frame = await setup_drawing_async(canvas)

last_frame_time = time.perf_counter()
frame_count = 0

while True:
while not glfw.window_should_close(canvas.window):
await asyncio.sleep(0.01)
# process inputs
glfw.poll_events()
# break on close
if glfw.window_should_close(window):
break
# draw a frame
canvas.draw_frame()
await draw_frame()
# present the frame to the screen
canvas.context.present()
# stats
Expand All @@ -51,10 +41,9 @@ async def main_loop():
print(f"{frame_count/etime:0.1f} FPS")
last_frame_time, frame_count = time.perf_counter(), 0

# dispose all resources and quit
glfw.hide_window(window)
glfw.destroy_window(window)
glfw.terminate()
# dispose resources
glfw.destroy_window(canvas.window)
poll_glfw_briefly()


if __name__ == "__main__":
Expand Down
12 changes: 9 additions & 3 deletions examples/gui_auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@

from wgpu.gui.auto import WgpuCanvas, run

from triangle import setup_triangle # noqa: F401, RUF100
from cube import setup_cube # noqa: F401, RUF100
from triangle import setup_drawing_sync
# from cube import setup_drawing_sync


canvas = WgpuCanvas(title=f"Triangle example on {WgpuCanvas.__name__}")
setup_triangle(canvas)
draw_frame = setup_drawing_sync(canvas)


@canvas.request_draw
def animate():
draw_frame()
canvas.request_draw()


if __name__ == "__main__":
Expand Down
57 changes: 25 additions & 32 deletions examples/gui_direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,65 +9,59 @@
# run_example = false

import time
import atexit

import glfw

from wgpu.backends.wgpu_native import GPUCanvasContext
from wgpu.gui.glfw import get_glfw_present_info, get_physical_size
from wgpu.gui.glfw import get_glfw_present_info, poll_glfw_briefly

from triangle import setup_triangle # noqa: F401, RUF100
from cube import setup_cube # noqa: F401, RUF100
# from triangle import setup_drawing_sync
from cube import setup_drawing_sync

# Setup glfw
glfw.init()
atexit.register(glfw.terminate)


class MinimalGlfwCanvas: # implements WgpuCanvasInterface
"""Minimal canvas interface implementation triangle.py has everything it needs to draw."""
"""Minimal canvas interface required by wgpu."""

def __init__(self, title):
# disable automatic API selection, we are not using opengl
glfw.window_hint(glfw.CLIENT_API, glfw.NO_API)
glfw.window_hint(glfw.RESIZABLE, True)

def __init__(self, window):
self._window = window
self.window = glfw.create_window(640, 480, title, None, None)
self.context = GPUCanvasContext(self)
self.draw_frame = None

def get_present_info(self):
"""get window and display id, includes some triage to deal with OS differences"""
return get_glfw_present_info(self._window)
return get_glfw_present_info(self.window)

def get_physical_size(self):
"""get framebuffer size in integer pixels"""
return get_physical_size(self._window)
psize = glfw.get_framebuffer_size(self.window)
return int(psize[0]), int(psize[1])

def get_context(self, kind="webgpu"):
return self.context

def request_draw(self, func=None):
# A method from WGPUCanvasBase that is called by triangle.py
if func is not None:
self.draw_frame = func


def main():
# create a window with glfw
glfw.init()
# disable automatic API selection, we are not using opengl
glfw.window_hint(glfw.CLIENT_API, glfw.NO_API)
glfw.window_hint(glfw.RESIZABLE, True)
window = glfw.create_window(640, 480, "wgou demo glfw direct", None, None)

# create canvas
canvas = MinimalGlfwCanvas(window)
setup_cube(canvas)
canvas = MinimalGlfwCanvas("wgpu gui direct")
draw_frame = setup_drawing_sync(canvas)

last_frame_time = time.perf_counter()
frame_count = 0

# render loop
while True:
while not glfw.window_should_close(canvas.window):
# process inputs
glfw.poll_events()
# break on close
if glfw.window_should_close(window):
break
# draw a frame
canvas.draw_frame()
draw_frame()
# present the frame to the screen
canvas.context.present()
# stats
Expand All @@ -77,10 +71,9 @@ def main():
print(f"{frame_count/etime:0.1f} FPS")
last_frame_time, frame_count = time.perf_counter(), 0

# dispose all resources and quit
glfw.destroy_window(window)
glfw.poll_events()
glfw.terminate()
# dispose resources
glfw.destroy_window(canvas.window)
poll_glfw_briefly()


if __name__ == "__main__":
Expand Down
12 changes: 9 additions & 3 deletions examples/gui_glfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@

from wgpu.gui.glfw import WgpuCanvas, run

from triangle import setup_triangle # noqa: F401, RUF100
from cube import setup_cube # noqa: F401, RUF100
from triangle import setup_drawing_sync
# from cube import setup_drawing_sync


canvas = WgpuCanvas(title=f"Triangle example on {WgpuCanvas.__name__}")
setup_triangle(canvas)
draw_frame = setup_drawing_sync(canvas)


@canvas.request_draw
def animate():
draw_frame()
canvas.request_draw()


if __name__ == "__main__":
Expand Down
Loading