Rust-SDL2 / rust-sdl2

SDL2 bindings for Rust
MIT License
2.65k stars 465 forks source link

drawer functions panic when user switches between windows in FULLSCREEN mode #420

Open evgenybf opened 9 years ago

evgenybf commented 9 years ago

Windows 7, 8.1

   let window = match sdl2::video::Window::new(&sdl_context, "Super Transball 2 v1.5 (Rust)", sdl2::video::WindowPos::PosCentered, 
            sdl2::video::WindowPos::PosCentered, SCREEN_X, SCREEN_Y, sdl2::video::OPENGL | sdl2::video::FULLSCREEN) {
   ...
   let mut renderer = match sdl2::render::Renderer::from_window(window, sdl2::render::RenderDriverIndex::Auto, sdl2::render::ACCELERATED) {
       Ok(renderer) => renderer,
       Err(err) => panic!(format!("failed to create renderer: {}", err))
   };

   loop {
   ...
      renderer.drawer().copy_ex(&texture, None, None, angle as f64, None, (false, false));

When user presses Alt+Tab it crashes with the following error message:

Running target\debug\my-hello-sdl.exe thread

panicked at 'Error copying texture (ex): BeginScene(): INVALIDCALL', ...sdl2-0.2.3\src/sdl2\render.rs:721 An unknown error occurred

AngryLawyer commented 9 years ago

Huh, that's a bit of a pain. I'l need to get access to a Windows computer to look at this.

nukep commented 9 years ago

My guess is that you're not calling renderer.drawer().present() by the end of the game loop. Let me know if that fixes the problem.

Nonetheless, this is probably an SDL2 bug; leaving out SDL_RenderPresent in the loop should probably do nothing as opposed to failing. The error seems to come from the Direct3D method BeginScene(), which is returning D3DERR_INVALIDCALL. According to the docs, this should only happen if BeginScene() was called twice without a EndScene() call. In the D3D driver, EndScene() gets called when SDL_RenderPresent is called. BeginScene() also seems to get called unconditionally after the device resets (such as when the user leaves fullscreen).

evgenybf commented 9 years ago

The code to reproduce this issue is here: https://github.com/evgenybf/rust-sdl2-fullscreen-panic

As nukep said:

My guess is that you're not calling renderer.drawer().present() by the end of the game loop. Let me know if that fixes the problem.

It's called at the end of each iteration:

        let mut drawer = renderer.drawer();
        drawer.set_draw_color(sdl2::pixels::Color::RGB(color_index, color_index, color_index));
        drawer.clear();
        drawer.copy_ex(&texture, None, None, angle as f64, None, (false, false));
        drawer.present();

As nukep said:

Nonetheless, this is probably an SDL2 bug; leaving out SDL_RenderPresent in the loop should probably do nothing as opposed to failing.

I agree that the main part of the issue is on SDL2 side and not in the binding... Actually, I should have tried to reproduce it from C and I'll do. But maybe drawer functions should not panic each time when they encounter something wrong?

nukep commented 9 years ago

Reproduced on my machine (Windows 8.1, 64-bit).

Interestingly, toggling fullscreen normally from the application (with SDL_SetWindowFullscreen / WindowProperties::set_fullscreen, using FTTrue, FTDesktop and FTOff) doesn't trigger the bug. I can only reproduce the bug by Alt+Tabbing out of the application.

Actually, I should have tried to reproduce it from C and I'll do.

If you can reproduce this in C, then you should definitely file the bug at https://bugzilla.libsdl.org/

But maybe drawer functions should not panic each time when they encounter something wrong?

If I'm correct, the Renderer functions in SDL2 should only fail on catastrophic driver failures. i.e. they create non-recoverable errors. In this case, ignoring the error shouldn't be allowed as doing so could potentially create more weird problems in the future. Additionally, adding .unwrap() or try! to every method might be overly redundant. :]

evgenybf commented 9 years ago

Some investigation results:

PS: I still don't understand why D3D_RenderCopyEx->D3D_ActivateRenderer data->beginScene==SDL_TRUE even if I call drawer.copy() before drawer.copy_ex() - the first copy() call should reset that flag... It needs debugging.

evgenybf commented 9 years ago

nukep wrote:

If I'm correct, the Renderer functions in SDL2 should only fail on catastrophic driver failures. .. Additionally, adding .unwrap() or try! to every method might be overly redundant. :]

In my case I could just ignore this error as it's not that catastrophic. On the other hand I agree that we don't have to make the library more complex on account of only the single case. So, I don't know... do as it covenient for you. Thanks for support!