Skip to content

Commit

Permalink
Tweak all examples
Browse files Browse the repository at this point in the history
  • Loading branch information
almarklein committed Oct 2, 2024
1 parent 0eee4e1 commit d546dc4
Show file tree
Hide file tree
Showing 13 changed files with 203 additions and 159 deletions.
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

0 comments on commit d546dc4

Please sign in to comment.