{ "cells": [ { "cell_type": "markdown", "id": "9b08294a", "metadata": {}, "source": [ "# WGPU example\n", "\n", "**Note that this example depends on wgpu-py (`pip install -U wgpu`), and may need an update if the API of wgpu changes.**\n", "\n", "An example using low-level wgpu code. The first cell is simply a copy of wgpu-py's triangle.py example. Tested against wgpu-py v0.11.0." ] }, { "cell_type": "code", "execution_count": 1, "id": "3d32ce94", "metadata": {}, "outputs": [], "source": [ "import wgpu\n", "\n", "\n", "# %% Shaders\n", "\n", "\n", "shader_source = \"\"\"\n", "struct VertexInput {\n", " @builtin(vertex_index) vertex_index : u32,\n", "};\n", "struct VertexOutput {\n", " @location(0) color : vec4,\n", " @builtin(position) pos: vec4,\n", "};\n", "\n", "@vertex\n", "fn vs_main(in: VertexInput) -> VertexOutput {\n", " var positions = array, 3>(\n", " vec2(0.0, -0.5),\n", " vec2(0.5, 0.5),\n", " vec2(-0.5, 0.75),\n", " );\n", " var colors = array, 3>( // srgb colors\n", " vec3(1.0, 1.0, 0.0),\n", " vec3(1.0, 0.0, 1.0),\n", " vec3(0.0, 1.0, 1.0),\n", " );\n", " let index = i32(in.vertex_index);\n", " var out: VertexOutput;\n", " out.pos = vec4(positions[index], 0.0, 1.0);\n", " out.color = vec4(colors[index], 1.0);\n", " return out;\n", "}\n", "\n", "@fragment\n", "fn fs_main(in: VertexOutput) -> @location(0) vec4 {\n", " let physical_color = pow(in.color.rgb, vec3(2.2)); // gamma correct\n", " return vec4(physical_color, in.color.a);\n", "}\n", "\"\"\"\n", "\n", "\n", "# %% The wgpu calls\n", "\n", "\n", "def main(canvas, power_preference=\"high-performance\", limits=None):\n", " \"\"\"Regular function to setup a viz on the given canvas.\"\"\"\n", " # Note: passing the canvas here can (oddly enough) prevent the\n", " # adapter from being found. Seen with wx/Linux.\n", " adapter = wgpu.request_adapter(canvas=None, power_preference=power_preference)\n", " device = adapter.request_device(required_limits=limits)\n", " return _main(canvas, device)\n", "\n", "\n", "async def main_async(canvas):\n", " \"\"\"Async function to setup a viz on the given canvas.\"\"\"\n", " adapter = await wgpu.request_adapter_async(\n", " canvas=canvas, power_preference=\"high-performance\"\n", " )\n", " device = await adapter.request_device_async(required_limits={})\n", " return _main(canvas, device)\n", "\n", "\n", "def _main(canvas, device):\n", " shader = device.create_shader_module(code=shader_source)\n", "\n", " # No bind group and layout, we should not create empty ones.\n", " pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])\n", "\n", " present_context = canvas.get_context()\n", " render_texture_format = present_context.get_preferred_format(device.adapter)\n", " present_context.configure(device=device, format=render_texture_format)\n", "\n", " render_pipeline = device.create_render_pipeline(\n", " layout=pipeline_layout,\n", " vertex={\n", " \"module\": shader,\n", " \"entry_point\": \"vs_main\",\n", " \"buffers\": [],\n", " },\n", " primitive={\n", " \"topology\": wgpu.PrimitiveTopology.triangle_list,\n", " \"front_face\": wgpu.FrontFace.ccw,\n", " \"cull_mode\": wgpu.CullMode.none,\n", " },\n", " depth_stencil=None,\n", " multisample=None,\n", " fragment={\n", " \"module\": shader,\n", " \"entry_point\": \"fs_main\",\n", " \"targets\": [\n", " {\n", " \"format\": render_texture_format,\n", " \"blend\": {\n", " \"color\": (\n", " wgpu.BlendFactor.one,\n", " wgpu.BlendFactor.zero,\n", " wgpu.BlendOperation.add,\n", " ),\n", " \"alpha\": (\n", " wgpu.BlendFactor.one,\n", " wgpu.BlendFactor.zero,\n", " wgpu.BlendOperation.add,\n", " ),\n", " },\n", " },\n", " ],\n", " },\n", " )\n", "\n", " def draw_frame():\n", " current_texture_view = present_context.get_current_texture()\n", " command_encoder = device.create_command_encoder()\n", "\n", " render_pass = command_encoder.begin_render_pass(\n", " color_attachments=[\n", " {\n", " \"view\": current_texture_view,\n", " \"resolve_target\": None,\n", " \"clear_value\": (0, 0, 0, 1),\n", " \"load_op\": wgpu.LoadOp.clear,\n", " \"store_op\": wgpu.StoreOp.store,\n", " }\n", " ],\n", " )\n", "\n", " render_pass.set_pipeline(render_pipeline)\n", " # render_pass.set_bind_group(0, no_bind_group, [], 0, 1)\n", " render_pass.draw(3, 1, 0, 0)\n", " render_pass.end()\n", " device.queue.submit([command_encoder.finish()])\n", "\n", " canvas.request_draw(draw_frame)\n", " return device" ] }, { "cell_type": "markdown", "id": "1c992985", "metadata": {}, "source": [ "Here we define a canvas. This should later be included in wgpu-py." ] }, { "cell_type": "code", "execution_count": 2, "id": "e7eee7d0", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "8c70a603f4854ca989359861927e6277", "version_major": 2, "version_minor": 0 }, "text/plain": [ "RFBOutputContext()" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "e4ed0de6e47a46ad93b45b8e7ec1086c", "version_major": 2, "version_minor": 0 }, "text/html": [ "
snapshot
" ], "text/plain": [ "JupyterWgpuCanvas()" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import wgpu.backends.rs\n", "from wgpu.gui.jupyter import WgpuCanvas\n", "\n", "canvas = WgpuCanvas()\n", "\n", "# Apply the triangle example to the canvas, and show it\n", "main(canvas)\n", "canvas" ] }, { "cell_type": "code", "execution_count": null, "id": "2d9cdcd6", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.9" } }, "nbformat": 4, "nbformat_minor": 5 }