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

Manual GLFW integration #480

Merged
merged 9 commits into from
Apr 11, 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
15 changes: 10 additions & 5 deletions examples/triangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ async def main_async(canvas):
return _main(canvas, device)


def _main(canvas, device):
def setup_draw(context, device):
"""Setup context and device for drawing a triangle and return draw callback"""
shader = device.create_shader_module(code=shader_source)

# No bind group and layout, we should not create empty ones.
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])

present_context = canvas.get_context()
render_texture_format = present_context.get_preferred_format(device.adapter)
present_context.configure(device=device, format=render_texture_format)
render_texture_format = context.get_preferred_format(device.adapter)
context.configure(device=device, format=render_texture_format)

render_pipeline = device.create_render_pipeline(
layout=pipeline_layout,
Expand Down Expand Up @@ -122,7 +122,7 @@ def _main(canvas, device):
)

def draw_frame():
current_texture = present_context.get_current_texture()
current_texture = context.get_current_texture()
command_encoder = device.create_command_encoder()

render_pass = command_encoder.begin_render_pass(
Expand All @@ -143,6 +143,11 @@ def draw_frame():
render_pass.end()
device.queue.submit([command_encoder.finish()])

return draw_frame


def _main(canvas, device):
draw_frame = setup_draw(canvas.get_context(), device)
canvas.request_draw(draw_frame)
return device

Expand Down
77 changes: 77 additions & 0 deletions examples/triangle_glfw_direct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
Direct integration of glfw and wgpu-py without using the
wgpu.gui Canvas abstraction/class hierarchy.
Demonstration for hardcore users that need total low-level
control.
# run_example = false
"""

import sys
from pathlib import Path

import glfw

from wgpu.backends.wgpu_native import GPUCanvasContext
from wgpu.gui.glfw import get_surface_info, get_physical_size
from wgpu.utils.device import get_default_device


sys.path.insert(0, str(Path(__file__).parent))

from triangle import setup_draw # noqa: E402


class GlfwCanvas:
Korijn marked this conversation as resolved.
Show resolved Hide resolved
"""Minimal canvas interface implementation to support GPUCanvasContext"""

def __init__(self, window):
self._window = window

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

def get_physical_size(self):
"""get framebuffer size in integer pixels"""
return get_physical_size(self._window)


def main():
# get the gpu device/adapter combo
device = get_default_device()

# 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, "glfw window", None, None)

# create a WGPU context
canvas = GlfwCanvas(window)
context = GPUCanvasContext(canvas)

# drawing logic
draw_frame = setup_draw(context, device)

# render loop
while True:
# draw a frame
draw_frame()
# present the frame to the screen
context.present()
# process inputs
glfw.poll_events()

# break on close
if glfw.window_should_close(window):
break

# dispose all resources and quit
glfw.terminate()


if __name__ == "__main__":
main()
62 changes: 35 additions & 27 deletions wgpu/gui/glfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,39 @@
}


def get_surface_info(window):
if sys.platform.startswith("win"):
return {
"platform": "windows",
"window": int(glfw.get_win32_window(window)),
}
elif sys.platform.startswith("darwin"):
return {
"platform": "cocoa",
"window": int(glfw.get_cocoa_window(window)),
}
elif sys.platform.startswith("linux"):
if is_wayland:
return {
"platform": "wayland",
"window": int(glfw.get_wayland_window(window)),
"display": int(glfw.get_wayland_display()),
}
else:
return {
"platform": "x11",
"window": int(glfw.get_x11_window(window)),
"display": int(glfw.get_x11_display()),
}
else:
raise RuntimeError(f"Cannot get GLFW surafce info on {sys.platform}.")


def get_physical_size(window):
psize = glfw.get_framebuffer_size(window)
return int(psize[0]), int(psize[1])


class GlfwWgpuCanvas(WgpuAutoGui, WgpuCanvasBase):
"""A glfw window providing a wgpu canvas."""

Expand Down Expand Up @@ -212,8 +245,7 @@ def _determine_size(self):
# on some systems and in logical-pixels on other, we use the
# framebuffer size and pixel ratio to derive the logical size.
pixel_ratio = get_window_content_scale(self._window)[0]
psize = glfw.get_framebuffer_size(self._window)
psize = int(psize[0]), int(psize[1])
psize = get_physical_size(self._window)

self._pixel_ratio = pixel_ratio
self._physical_size = psize
Expand Down Expand Up @@ -266,31 +298,7 @@ def _set_logical_size(self, new_logical_size):
# API

def get_surface_info(self):
if sys.platform.startswith("win"):
return {
"platform": "windows",
"window": int(glfw.get_win32_window(self._window)),
}
elif sys.platform.startswith("darwin"):
return {
"platform": "cocoa",
"window": int(glfw.get_cocoa_window(self._window)),
}
elif sys.platform.startswith("linux"):
if is_wayland:
return {
"platform": "wayland",
"window": int(glfw.get_wayland_window(self._window)),
"display": int(glfw.get_wayland_display()),
}
else:
return {
"platform": "x11",
"window": int(glfw.get_x11_window(self._window)),
"display": int(glfw.get_x11_display()),
}
else:
raise RuntimeError(f"Cannot get GLFW surafce info on {sys.platform}.")
return get_surface_info(self._window)

def get_pixel_ratio(self):
return self._pixel_ratio
Expand Down
Loading