From a8efa5a70fa725198e91fd2605805e5e3615db65 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 28 May 2026 22:33:14 +0200 Subject: [PATCH 1/9] serialize the existing triangle, plus cleanup default args --- examples/triangle.py | 178 +++++++++++++------------------------------ 1 file changed, 54 insertions(+), 124 deletions(-) diff --git a/examples/triangle.py b/examples/triangle.py index 8bb05d97..d5bc42ce 100644 --- a/examples/triangle.py +++ b/examples/triangle.py @@ -5,134 +5,25 @@ visualisation because it does not need buffers or textures. The same example in other languages / API's: +* JavaScript WebGPU: + https://webgpu.github.io/webgpu-samples/?sample=helloTriangle * Rust wgpu: - https://github.com/gfx-rs/wgpu-rs/blob/master/examples/hello-triangle/main.rs + https://github.com/gfx-rs/wgpu/blob/trunk/examples/features/src/hello_triangle/mod.rs * C wgpu: - https://github.com/gfx-rs/wgpu/blob/master/examples/triangle/main.c + https://github.com/gfx-rs/wgpu-native/blob/trunk/examples/triangle/main.c * Python Vulkan: https://github.com/realitix/vulkan/blob/master/example/contribs/example_glfw.py -This example is set up so it can be run with any canvas. Running this file -as a script will use rendercanvas with the auto-backend. - +This example is meant as a standalone starting point. And is therefore as minimal as possible. """ -from typing import Callable - import wgpu -# %% Entrypoints (sync and async) - - -def setup_drawing_sync( - context, power_preference="high-performance", limits=None, format=None -) -> Callable: - """Setup to draw a triangle on the given context. - - Returns the draw function. - """ - - adapter = wgpu.gpu.request_adapter_sync(power_preference=power_preference) - device = adapter.request_device_sync(required_limits=limits) - - pipeline_kwargs = get_render_pipeline_kwargs(context, device, format) - - render_pipeline = device.create_render_pipeline(**pipeline_kwargs) - - return get_draw_function(context, device, render_pipeline, asynchronous=False) - - -async def setup_drawing_async(context, limits=None, format=None) -> Callable: - """Setup to async-draw a triangle on the given context. - - 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_kwargs = get_render_pipeline_kwargs(context, device, format) - - render_pipeline = await device.create_render_pipeline_async(**pipeline_kwargs) - - return get_draw_function(context, device, render_pipeline, asynchronous=True) - - -# %% Functions to create wgpu objects - - -def get_render_pipeline_kwargs( - context, device, render_texture_format -) -> wgpu.RenderPipelineDescriptor: - if render_texture_format is None: - render_texture_format = context.get_preferred_format(device.adapter) - context.configure(device=device, format=render_texture_format) - - shader = device.create_shader_module(code=shader_source) - pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[]) - - return wgpu.RenderPipelineDescriptor( - layout=pipeline_layout, - vertex=wgpu.VertexState( - module=shader, - entry_point="vs_main", - ), - depth_stencil=None, - multisample=None, - fragment=wgpu.FragmentState( - module=shader, - entry_point="fs_main", - targets=[ - wgpu.ColorTargetState( - format=render_texture_format, - blend={"color": {}, "alpha": {}}, - ) - ], - ), - ) - - -def get_draw_function( - context, - device: wgpu.GPUDevice, - render_pipeline: wgpu.GPURenderPipeline, - *, - asynchronous: bool, -) -> Callable: - def draw_frame_sync(): - current_texture = context.get_current_texture() - command_encoder = device.create_command_encoder() - - render_pass = command_encoder.begin_render_pass( - color_attachments=[ - wgpu.RenderPassColorAttachment( - view=current_texture.create_view(), - resolve_target=None, - clear_value=(0, 0, 0, 1), - load_op="clear", - store_op="store", - ) - ], - ) - - render_pass.set_pipeline(render_pipeline) - # render_pass.set_bind_group(0, no_bind_group) - render_pass.draw(3, 1, 0, 0) - render_pass.end() - device.queue.submit([command_encoder.finish()]) - - async def draw_frame_async(): - draw_frame_sync() # nothing async here - - if asynchronous: - return draw_frame_async - else: - return draw_frame_sync - - -# %% WGSL +from rendercanvas.auto import RenderCanvas, loop +# the shader code is provided as a string literal for protability +# TODO: maybe simplify in several ways, or make it the red triangle. shader_source = """ struct VertexInput { @builtin(vertex_index) vertex_index : u32, @@ -169,12 +60,51 @@ async def draw_frame_async(): """ -if __name__ == "__main__": - from rendercanvas.auto import RenderCanvas, loop - - canvas = RenderCanvas(size=(640, 480), title="wgpu triangle example") - context = canvas.get_wgpu_context() +# adapter provides allows us to create a single device, which is the general entrypoint to create most wgpu objects. +adapter = wgpu.gpu.request_adapter_sync() +device = adapter.request_device_sync() + +# setting up a canvas, so we can see what we draw +canvas = RenderCanvas(size=(640, 480), title="wgpu triangle example") +context = canvas.get_wgpu_context() +render_texture_format = context.get_preferred_format(device.adapter) +context.configure(device=device, format=render_texture_format) + +# creating the shader module compiles the shader code for your GPU. +shader = device.create_shader_module(code=shader_source) + +render_pipeline = device.create_render_pipeline(**wgpu.RenderPipelineDescriptor( + layout=wgpu.AutoLayoutMode.auto, + vertex=wgpu.VertexState(module=shader), + depth_stencil=None, + multisample=None, + fragment=wgpu.FragmentState( + module=shader, + targets=[ + wgpu.ColorTargetState(format=render_texture_format) + ], + ), +)) + +def drawing_function(): + command_encoder = device.create_command_encoder() + current_texture_view: wgpu.GPUTextureView = context.get_current_texture().create_view() #TODO: there is no type hint for GPUTexture from the rendercanvas side. + + render_pass = command_encoder.begin_render_pass( + color_attachments=[ + wgpu.RenderPassColorAttachment( + view=current_texture_view, + clear_value=(0, 1, 0, 1), # a green background + load_op=wgpu.LoadOp.clear, + store_op=wgpu.StoreOp.store, + ) + ], + ) + render_pass.set_pipeline(render_pipeline) + render_pass.draw(3) + render_pass.end() + device.queue.submit([command_encoder.finish()]) - draw_frame = setup_drawing_sync(context) - canvas.request_draw(draw_frame) +if __name__ == "__main__": + canvas.request_draw(drawing_function) loop.run() From dbdd243966919d93fbf4e6e5c8042171e2a32ac3 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 28 May 2026 22:45:07 +0200 Subject: [PATCH 2/9] simplify the shadercode a little bit --- examples/triangle.py | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/examples/triangle.py b/examples/triangle.py index d5bc42ce..14a276c9 100644 --- a/examples/triangle.py +++ b/examples/triangle.py @@ -25,37 +25,34 @@ # the shader code is provided as a string literal for protability # TODO: maybe simplify in several ways, or make it the red triangle. shader_source = """ -struct VertexInput { - @builtin(vertex_index) vertex_index : u32, -}; struct VertexOutput { - @location(0) color : vec4, - @builtin(position) pos: vec4, + @location(0) color : vec4f, + @builtin(position) pos: vec4f, }; @vertex -fn vs_main(in: VertexInput) -> VertexOutput { - var positions = array, 3>( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.75), +fn vs_main(@builtin(vertex_index) index: u32) -> VertexOutput { + var positions = array( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.75), ); - var colors = array, 3>( // srgb colors - vec3(1.0, 1.0, 0.0), - vec3(1.0, 0.0, 1.0), - vec3(0.0, 1.0, 1.0), + // vertex attributes are interpolated in the fragment shader. + var colors = array( // srgb colors + vec3(1.0, 1.0, 0.0), + vec3(1.0, 0.0, 1.0), + vec3(0.0, 1.0, 1.0), ); - let index = i32(in.vertex_index); var out: VertexOutput; - out.pos = vec4(positions[index], 0.0, 1.0); - out.color = vec4(colors[index], 1.0); + out.pos = vec4(positions[index], 0.0, 1.0); + out.color = vec4(colors[index], 1.0); return out; } @fragment -fn fs_main(in: VertexOutput) -> @location(0) vec4 { - let physical_color = pow(in.color.rgb, vec3(2.2)); // gamma correct - return vec4(physical_color, in.color.a); +fn fs_main(in: VertexOutput) -> @location(0) vec4f { + let physical_color = pow(in.color.rgb, vec3(2.2)); // gamma correct + return vec4(physical_color, in.color.a); } """ From 3d65f3c65684735a0e2b4be59c8e47c6e64cc896 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 28 May 2026 22:54:37 +0200 Subject: [PATCH 3/9] ruff format --- examples/triangle.py | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/examples/triangle.py b/examples/triangle.py index 14a276c9..6afd3860 100644 --- a/examples/triangle.py +++ b/examples/triangle.py @@ -24,7 +24,7 @@ # the shader code is provided as a string literal for protability # TODO: maybe simplify in several ways, or make it the red triangle. -shader_source = """ +wgsl_shader_source = """ struct VertexOutput { @location(0) color : vec4f, @builtin(position) pos: vec4f, @@ -68,30 +68,31 @@ context.configure(device=device, format=render_texture_format) # creating the shader module compiles the shader code for your GPU. -shader = device.create_shader_module(code=shader_source) - -render_pipeline = device.create_render_pipeline(**wgpu.RenderPipelineDescriptor( - layout=wgpu.AutoLayoutMode.auto, - vertex=wgpu.VertexState(module=shader), - depth_stencil=None, - multisample=None, - fragment=wgpu.FragmentState( - module=shader, - targets=[ - wgpu.ColorTargetState(format=render_texture_format) - ], - ), -)) +shader = device.create_shader_module(code=wgsl_shader_source) + +render_pipeline = device.create_render_pipeline( + **wgpu.RenderPipelineDescriptor( + layout=wgpu.AutoLayoutMode.auto, + vertex=wgpu.VertexState(module=shader), + depth_stencil=None, + multisample=None, + fragment=wgpu.FragmentState( + module=shader, + targets=[wgpu.ColorTargetState(format=render_texture_format)], + ), + ) +) + def drawing_function(): command_encoder = device.create_command_encoder() - current_texture_view: wgpu.GPUTextureView = context.get_current_texture().create_view() #TODO: there is no type hint for GPUTexture from the rendercanvas side. + current_texture_view = context.get_current_texture().create_view() render_pass = command_encoder.begin_render_pass( color_attachments=[ wgpu.RenderPassColorAttachment( view=current_texture_view, - clear_value=(0, 1, 0, 1), # a green background + clear_value=(0, 1, 0, 1), # a green background load_op=wgpu.LoadOp.clear, store_op=wgpu.StoreOp.store, ) @@ -102,6 +103,7 @@ def drawing_function(): render_pass.end() device.queue.submit([command_encoder.finish()]) + if __name__ == "__main__": canvas.request_draw(drawing_function) loop.run() From 3230cbe02ffd1a2aedd3793b13fdcc2faab8d5ef Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 28 May 2026 23:05:31 +0200 Subject: [PATCH 4/9] switch auto gui example to cube --- examples/gui_auto.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/gui_auto.py b/examples/gui_auto.py index f1f728bf..91f046de 100644 --- a/examples/gui_auto.py +++ b/examples/gui_auto.py @@ -1,5 +1,5 @@ """ -Run the triangle example in an automatically selected GUI backend. +Run the cube example in an automatically selected GUI backend. The rendercanvas automatically selects one of its available GUI backends. E.g. running this in a notebook will use the @@ -17,9 +17,9 @@ from rendercanvas.auto import RenderCanvas, loop try: - from .triangle import setup_drawing_sync + from .cube import setup_drawing_sync except ImportError: - from triangle import setup_drawing_sync + from cube import setup_drawing_sync canvas = RenderCanvas(title="Cube example on $backend") draw_frame = setup_drawing_sync(canvas.get_wgpu_context()) From 42f52c60f7d2d87f52a3aaf73e3ad690bdfd2e30 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 31 May 2026 02:18:45 +0200 Subject: [PATCH 5/9] hdr example to use cube --- examples/cube.py | 9 +++++---- examples/offscreen_hdr.py | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/cube.py b/examples/cube.py index 607a76b9..34c68f2b 100644 --- a/examples/cube.py +++ b/examples/cube.py @@ -24,7 +24,7 @@ def setup_drawing_sync( - context, power_preference="high-performance", limits=None + context, power_preference="high-performance", limits=None, format=None ) -> Callable: """Setup to draw a rotating cube on the given context. @@ -38,7 +38,7 @@ def setup_drawing_sync( ) pipeline_layout, uniform_buffer, bind_group = create_pipeline_layout(device) - pipeline_kwargs = get_render_pipeline_kwargs(context, device, pipeline_layout) + pipeline_kwargs = get_render_pipeline_kwargs(context, device, pipeline_layout, render_texture_format=format) render_pipeline = device.create_render_pipeline(**pipeline_kwargs) @@ -84,9 +84,10 @@ def get_drawing_func(context, device): def get_render_pipeline_kwargs( - context, device: wgpu.GPUDevice, pipeline_layout: wgpu.GPUPipelineLayout + context, device: wgpu.GPUDevice, pipeline_layout: wgpu.GPUPipelineLayout, render_texture_format: wgpu.TextureFormat|None = None ) -> wgpu.RenderPipelineDescriptor: - render_texture_format = context.get_preferred_format(device.adapter) + if render_texture_format is None: + render_texture_format = context.get_preferred_format(device.adapter) context.configure(device=device, format=render_texture_format) shader = device.create_shader_module( diff --git a/examples/offscreen_hdr.py b/examples/offscreen_hdr.py index 54d87bc5..9128469d 100644 --- a/examples/offscreen_hdr.py +++ b/examples/offscreen_hdr.py @@ -16,9 +16,9 @@ from rendercanvas.offscreen import RenderCanvas try: - from .triangle import setup_drawing_sync + from .cube import setup_drawing_sync except ImportError: - from triangle import setup_drawing_sync + from cube import setup_drawing_sync canvas = RenderCanvas(size=(640, 480), pixel_ratio=2) From d11ba235c248e926fe41481be2d416fec489ebf7 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 31 May 2026 02:49:58 +0200 Subject: [PATCH 6/9] adjust comments --- examples/triangle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/triangle.py b/examples/triangle.py index 6afd3860..ecdbcd55 100644 --- a/examples/triangle.py +++ b/examples/triangle.py @@ -23,7 +23,6 @@ # the shader code is provided as a string literal for protability -# TODO: maybe simplify in several ways, or make it the red triangle. wgsl_shader_source = """ struct VertexOutput { @location(0) color : vec4f, @@ -58,6 +57,7 @@ # adapter provides allows us to create a single device, which is the general entrypoint to create most wgpu objects. +# for convenience and interoperability `wgpu.utils.get_default_device()` and associated configuration are provided. adapter = wgpu.gpu.request_adapter_sync() device = adapter.request_device_sync() @@ -83,7 +83,7 @@ ) ) - +# this function gets called for every frame. It ends with submitting a buffer of work onto the GPU queue. def drawing_function(): command_encoder = device.create_command_encoder() current_texture_view = context.get_current_texture().create_view() From 4bedb4ed67e43c6c4abe72766298581782b4d88d Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 31 May 2026 02:58:43 +0200 Subject: [PATCH 7/9] explain kwarg api diff --- examples/triangle.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/triangle.py b/examples/triangle.py index ecdbcd55..2ec4418e 100644 --- a/examples/triangle.py +++ b/examples/triangle.py @@ -70,6 +70,8 @@ # creating the shader module compiles the shader code for your GPU. shader = device.create_shader_module(code=wgsl_shader_source) +# in wgpu-py, methods that take descriptors will take the keyword arguments instead. +# descriptors and other structs can still be accessed via wgpu.structs or top level wgpu. render_pipeline = device.create_render_pipeline( **wgpu.RenderPipelineDescriptor( layout=wgpu.AutoLayoutMode.auto, From 31510adc089efeca16b27077da5027a07efb48ca Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 31 May 2026 03:02:36 +0200 Subject: [PATCH 8/9] ruff format --- examples/cube.py | 9 +++++++-- examples/triangle.py | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/cube.py b/examples/cube.py index 34c68f2b..868e6528 100644 --- a/examples/cube.py +++ b/examples/cube.py @@ -38,7 +38,9 @@ def setup_drawing_sync( ) pipeline_layout, uniform_buffer, bind_group = create_pipeline_layout(device) - pipeline_kwargs = get_render_pipeline_kwargs(context, device, pipeline_layout, render_texture_format=format) + pipeline_kwargs = get_render_pipeline_kwargs( + context, device, pipeline_layout, render_texture_format=format + ) render_pipeline = device.create_render_pipeline(**pipeline_kwargs) @@ -84,7 +86,10 @@ def get_drawing_func(context, device): def get_render_pipeline_kwargs( - context, device: wgpu.GPUDevice, pipeline_layout: wgpu.GPUPipelineLayout, render_texture_format: wgpu.TextureFormat|None = None + context, + device: wgpu.GPUDevice, + pipeline_layout: wgpu.GPUPipelineLayout, + render_texture_format: wgpu.TextureFormat | None = None, ) -> wgpu.RenderPipelineDescriptor: if render_texture_format is None: render_texture_format = context.get_preferred_format(device.adapter) diff --git a/examples/triangle.py b/examples/triangle.py index 2ec4418e..248761a1 100644 --- a/examples/triangle.py +++ b/examples/triangle.py @@ -85,6 +85,7 @@ ) ) + # this function gets called for every frame. It ends with submitting a buffer of work onto the GPU queue. def drawing_function(): command_encoder = device.create_command_encoder() From 58d2593601fe58add8f0652cfb36fb2cf7c96bbf Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 31 May 2026 03:09:29 +0200 Subject: [PATCH 9/9] skip screenshot for gui_auto --- examples/gui_auto.py | 2 -- examples/screenshots/gui_auto.png | Bin 25721 -> 0 bytes 2 files changed, 2 deletions(-) delete mode 100644 examples/screenshots/gui_auto.png diff --git a/examples/gui_auto.py b/examples/gui_auto.py index 91f046de..6c8216d9 100644 --- a/examples/gui_auto.py +++ b/examples/gui_auto.py @@ -12,8 +12,6 @@ """ -# test_example = true - from rendercanvas.auto import RenderCanvas, loop try: diff --git a/examples/screenshots/gui_auto.png b/examples/screenshots/gui_auto.png deleted file mode 100644 index 643c39773d5338f94022a699e3f9f802d5b48031..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25721 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 MY;m*n54WfP13#pH)Bpeg