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

Windows misreporting high GPU usage #251

Open
MatinAniss opened this issue Jan 22, 2025 · 2 comments
Open

Windows misreporting high GPU usage #251

MatinAniss opened this issue Jan 22, 2025 · 2 comments

Comments

@MatinAniss
Copy link
Contributor

MatinAniss commented Jan 22, 2025

Task manager seems to misreport the GPU usage of a GPUI app using the blade Vulkan backend at 80-100% GPU 3D usage. I also see this happen with the bunnymark example, but not the particle example.

This occurs on my machine when using the default Nvidia 3D setting Vulkan/OpenGl present method set to Auto.
Image

If I set the Nvidia 3D setting Vulkan/OpenGl present method to Prefer layered on DXGI Swapchain. It results with the GPUI application being reported at just 3% GPU 3D usage in Task manager. This seems much more reasonable.
Image

I am not sure why a DirectX swapchain would make Task manager correctly report the GPU usage.

@kvark
Copy link
Owner

kvark commented Feb 1, 2025

Sounds like it's just about the presence of vertical sync. Bunnymark is a benchmark, hence it doesn't have vsync enabled -

display_sync: gpu::DisplaySync::Recent,

Are you sure that Windows is misreporting this? Perhaps, it's just a bug in Zed's Windows platform that ends up shooting frames as fast as possible without a proper frequency of updates.

@MatinAniss
Copy link
Contributor Author

MatinAniss commented Feb 2, 2025

30 redraw requests example (32 millis sleep delay)

30.mp4

180 redraw requests example (5 millis sleep delay)

180.mp4

I don't have the slightest clue but covering the window with another the window seems to make the usage rise, these two tests were done with release mode but when using debug mode it reports at a constant 100% when being covered by another window.

I'm not too sure if this is remotely close to the correct way to test it but I just added a thread sleep in the winit AboutToWait event to prevent it from going as fast as possible to somewhat simulate it, Zed/GPUI handles this by calling the blocking DwmFlush win32 function which just syncs up with the monitor refresh rate that DWM wants.

The code doesn't actually draw anything on the screen just acquires a frame and presents it. I'm not sure if this reported usage is reasonable or not.

Winit + blade testing code
use std::time::{Duration, Instant};

use blade_graphics::{self as gpu, CommandEncoderDesc, Extent};

static mut COUNT: i32 = 0;
static mut EPOCH: Option<Instant> = None;

fn count_frame() {
    unsafe {
        COUNT += 1;

        if let Some(epoch) = EPOCH {
            let elapsed = epoch.elapsed();
            if elapsed >= Duration::from_secs(1) {
                println!("{} redraw requests per second", COUNT);
                COUNT = 0;
                EPOCH = Some(Instant::now());
            }
        } else {
            EPOCH = Some(Instant::now());
        }
    }
}

fn main() {
    let event_loop = winit::event_loop::EventLoop::new().unwrap();
    let window_attributes =
        winit::window::Window::default_attributes().with_title("blade-usage-issue");
    let window = event_loop.create_window(window_attributes).unwrap();

    let context = unsafe {
        gpu::Context::init(gpu::ContextDesc {
            presentation: true,
            validation: false,
            timing: false,
            capture: false,
            overlay: true,
            device_id: 0,
        })
        .unwrap()
    };

    let mut surface = context
        .create_surface_configured(
            &window,
            gpu::SurfaceConfig {
                size: gpu::Extent {
                    width: 1,
                    height: 1,
                    depth: 1,
                },
                usage: gpu::TextureUsage::TARGET,
                display_sync: gpu::DisplaySync::Recent,
                ..Default::default()
            },
        )
        .unwrap();

    let mut command_encoder = context.create_command_encoder(CommandEncoderDesc {
        name: "main",
        buffer_count: 2,
    });

    event_loop
        .run(|event, target| {
            target.set_control_flow(winit::event_loop::ControlFlow::Poll);
            match event {
                winit::event::Event::AboutToWait => {
                    window.request_redraw();

                    // Delay before requesting a new frame
                    std::thread::sleep(Duration::from_millis(32));
                }
                winit::event::Event::WindowEvent { event, .. } => match event {
                    winit::event::WindowEvent::Resized(size) => {
                        let config = gpu::SurfaceConfig {
                            size: Extent {
                                height: size.height,
                                width: size.width,
                                depth: 1,
                            },
                            usage: gpu::TextureUsage::TARGET,
                            display_sync: gpu::DisplaySync::Recent,
                            ..Default::default()
                        };
                        context.reconfigure_surface(&mut surface, config);
                    }
                    winit::event::WindowEvent::CloseRequested => {
                        target.exit();
                    }
                    winit::event::WindowEvent::RedrawRequested => {
                        count_frame();
                        let frame = surface.acquire_frame();
                        command_encoder.start();
                        command_encoder.present(frame);
                        context.submit(&mut command_encoder);
                    }
                    _ => {}
                },
                _ => {}
            }
        })
        .unwrap();
}

Using DisplaySync::Block does fix this problem, I'm guessing the reason Zed/GPUI does not use this is because of some added latency.

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

No branches or pull requests

2 participants