Skip to content

Improve get_current_texture for window resizing #705

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

Merged
merged 8 commits into from
Apr 15, 2025
Merged

Conversation

almarklein
Copy link
Member

Ref pygfx/pygfx#642

This PR improves the logic for canvas_context.get_current_texture(), especially with regard to window resizing. This covers a use-case where on some Linux systems the texture status is 'outdated`.

One problem is that we can get in a situation where we cannot successfully obtain a texture for the surface. There are a few options:

  • I looked into introducing a special error type that can be used to "cancel" a draw, to be caught by the scheduling logic. But this is tricky for various reasons, so I decided against it.
  • Define a special error type that the caller of get_current_texture() must catch. The downside is that it changes the API of the method compared to the WebGPU spec.
  • Use the texture regardless of it being in an error state. I don't know if there is a texture with status 'outdated', and if there is, I don't know what happens when you try to use it as a render target.
  • Return a dummy texture that will not actually get presented. As long as these cases are sporadic, this seems like an ok solution. The downside being that the code seems to be rendering, but no new data appears on screen. So we need to check that the texture status becomes successful within a few draws.

@almarklein
Copy link
Member Author

almarklein commented Apr 14, 2025

@ezorzin this is an attempt to avoid the resize errors you are having. Could you please help me test this? In this case that means installing wgpu-py from the repo and checking out this branch:

git clone https://github.com/pygfx/wgpu-py.git
cd wgpu-py
pip install -e .   # this will download the corresponding wgpu-native libs
git checkout surface-resize

Then run the triangle example, forcing glfw and using rendercanvas:

if __name__ == "__main__":
    from rendercanvas.glfw import RenderCanvas, loop

    canvas = RenderCanvas(size=(640, 480), title="wgpu triangle example")
    canvas.testflag = 0
    draw_frame = setup_drawing_sync(canvas)
    canvas.request_draw(draw_frame)
    loop.run()

Could you try with setting the canvas.testflag to 0, 1, and 2? I suspect that with the info that we collect this way, I can finish this PR and finally fix the error.

@almarklein
Copy link
Member Author

Actually, let's use rendercanvas, because it also draws during resizing with glfw, and wgpu does not. I updated the comment above.

@ezorzin
Copy link

ezorzin commented Apr 14, 2025

I tried, these are the results for the three canvas.testflag values:

canvas.testflag = 0:

No config found!
EGL says it can present to the window but not natively
Max vertex attribute stride unknown. Assuming it is 2048
GPU: NVIDIA GeForce GTX 1060 6GB
Vendor: NVIDIA
Driver Version: 550.120
Backend: Vulkan
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Segmentation fault (core dumped)

canvas.testflag = 1:
No config found!
EGL says it can present to the window but not natively
Max vertex attribute stride unknown. Assuming it is 2048
GPU: NVIDIA GeForce GTX 1060 6GB
Vendor: NVIDIA
Driver Version: 550.120
Backend: Vulkan
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Segmentation fault (core dumped)

canvas.testflag = 2:
No config found!
EGL says it can present to the window but not natively
Max vertex attribute stride unknown. Assuming it is 2048
GPU: NVIDIA GeForce GTX 1060 6GB
Vendor: NVIDIA
Driver Version: 550.120
Backend: Vulkan
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (5x), using dummy texture
Retry getting surface texture (status was 'Outdated').
No succesful surface texture obtained for 6 frames: 'Outdated'
Surface texture is 'Outdated' (0x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
Segmentation fault (core dumped)

The case canvas.testflag = 0 looks "better", in the sense that there is little text output while I resize the window.

In the two other cases there is a lot more text output and, after the first resize action, the text output continues printing by itself also if I stop resizing the window. Moreover, in these two latter cases there is also an artefact in the redrawing, that stays there after I stop resizing:
image

@almarklein
Copy link
Member Author

Awesome!

I realize that I made an oversight for the case that testflag > 0. I justed pushed a fix. Could you git pull surface-resize and try again for testflag == 1?

@ezorzin
Copy link

ezorzin commented Apr 14, 2025

testflag = 1:

No config found!
EGL says it can present to the window but not natively
Max vertex attribute stride unknown. Assuming it is 2048
GPU: NVIDIA GeForce GTX 1060 6GB
Vendor: NVIDIA
Driver Version: 550.120
Backend: Vulkan
_on_window_dirty
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (3x), using dummy texture
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (4x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (1x), using dummy texture
_on_size_change
_on_window_dirty
Retry getting surface texture (status was 'Outdated').
Surface texture is 'Outdated' (2x), using dummy texture
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
_on_size_change
_on_window_dirty
<class 'glfwcanvas.GlfwRenderCanvas'>
Segmentation fault (core dumped)

this time there is no redrawing artefact, no "printouts by itself".

@almarklein
Copy link
Member Author

almarklein commented Apr 15, 2025

A bit of background on the testflag: With testflag=1 I wanted to see whether on your system it works better if we don't reconfigure immediately when we detect that the size has changed. It looks like it does not matter, or that doing it results in less misses.

With testflag=2 I wanted to see if the texture was perhaps usable when the status was 'outdated'. It appears it is not.


So I cleaned this up. @ezorzin Can I ask you one more time to:

  • Update to the latest version rendercanvas (just released v2.0.4)
  • Update this branch to the latest commit.
  • Run the cube/triangle example with from rendercanvas.glfw import ....

If all is well, things should run smooth, without warnings, errors, or segfaults.

@almarklein almarklein marked this pull request as ready for review April 15, 2025 07:46
@almarklein almarklein requested a review from Korijn as a code owner April 15, 2025 07:46
@ezorzin
Copy link

ezorzin commented Apr 15, 2025

I upgraded rendercanvas to 2.0.4 and pulled the https://github.com/pygfx/wgpu-py.git repository to latest commit (12db73b) on the surface-resize branch. This is my pip list in my python's virtual environment:

Package      Version Editable project location
------------ ------- --------------------------------------------------
cffi         1.17.1
glfw         2.8.0
numpy        2.2.4
pip          24.0
pycparser    2.22
rendercanvas 2.0.4
sniffio      1.3.1
wgpu         0.21.1  /media/ezor/KRYPTON/WebgpuCodes/pymarching/wgpu-py

Then I run triangle.py by using this code snippet:

...
if __name__ == "__main__":
    from rendercanvas.glfw import RenderCanvas, loop

    canvas = RenderCanvas(size=(640, 480), title="wgpu triangle example")
    
    # EZOR-16APR2025: tested with 0, 1 and 2, then commented out
    #canvas.testflag = 0

    draw_frame = setup_drawing_sync(canvas)
    canvas.request_draw(draw_frame)
    loop.run()

and cube.py with this code snippet:

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

# EZOR-16APR2025: commented out
#canvas.request_draw(animate)

if __name__ == "__main__":
    #run()
    from rendercanvas.glfw import RenderCanvas, loop

    canvas = RenderCanvas(size=(640, 480), title="wgpu triangle example")
    
    # EZOR-16APR2025: tested with 0, 1 and 2, then commented out
    #canvas.testflag = 0
    
    draw_frame = setup_drawing_sync(canvas)

    # EZOR-16APR2025: commented out
    #canvas.request_draw(draw_frame)

    # EZOR-16APR2025: added
    canvas.request_draw(animate)

    loop.run()

I run both programs testing them with the canvas.testflag = 0, 1 or 2 and then I also commented it out (I believe you disabled that testing functionality in your latest release).

The results are good! No error printouts, no segmentation faults, no resizing artefacts. I tried resizing, "crazy fast" resizing, window minimizing, window maximizing: it looks this time it works!

These are the text outputs of the programs when running:
triangle.py:

No config found!
EGL says it can present to the window but not natively
Max vertex attribute stride unknown. Assuming it is 2048
GPU: NVIDIA GeForce GTX 1060 6GB
Vendor: NVIDIA
Driver Version: 550.120
Backend: Vulkan

cube.py:

Available adapters on this system:
No config found!
EGL says it can present to the window but not natively
Max vertex attribute stride unknown. Assuming it is 2048
Max vertex attribute stride unknown. Assuming it is 2048
NVIDIA GeForce GTX 1060 6GB (DiscreteGPU) via Vulkan
llvmpipe (LLVM 19.1.1, 256 bits) (CPU) via Vulkan
NVIDIA GeForce GTX 1060 6GB/PCIe/SSE2 (Unknown) via OpenGL
Max vertex attribute stride unknown. Assuming it is 2048

@Korijn
Copy link
Collaborator

Korijn commented Apr 15, 2025

Thank you for sticking with us!

@almarklein
Copy link
Member Author

almarklein commented Apr 15, 2025

Woohoo! 🚀

@ezorzin I really appreciate the many times you ran my tests. It has made it possible to find the proper way to deal with the problem(s), while not being able to reproduce it. Grazie mille!

@ezorzin
Copy link

ezorzin commented Apr 15, 2025

It's my pleasure! Wgpu made Vulkan easy, you made Wgpu easy in Python: easy^2 :) Thanks!

@almarklein almarklein merged commit bd54418 into main Apr 15, 2025
20 checks passed
@almarklein almarklein deleted the surface-resize branch April 15, 2025 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants