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

Stack overflow when deallocating PictureRecorder #597

Open
samizdatco opened this issue Dec 15, 2021 · 0 comments
Open

Stack overflow when deallocating PictureRecorder #597

samizdatco opened this issue Dec 15, 2021 · 0 comments

Comments

@samizdatco
Copy link
Contributor

samizdatco commented Dec 15, 2021

While investigating this issue reporting silent crashes during image exports I’ve come across a reproducible stack overflow when a PictureRecorder goes out of scope.

The example code below is a little contrived but mirrors the way I’m attempting to make PictureRecorder ‘restartable’, such that a picture can be generated and then additional drawing commands can be appended to it. My approach has been to start a new recording and immediately draw the output Picture to it, then start directing client commands to that new recording.

When Pictures are generated repeatedly, there are increasing levels of nesting, since each ‘restart’ of the PictureRecorder draws the Picture of its previous state (which in turn draws the Picture of its previous state, and so on). Note that It’s quite possible that this is an anti-pattern that I should abandon and if so would love suggestions for alternative approaches.

Here’s a test case that reliably triggers a stack overflow for me (though it wouldn’t surprise me if you need to adjust the loop range above 20k on other systems):

fn recorder_test() {
  use skia_safe::{Rect, PictureRecorder, Paint};
  let paint = Paint::default();
  let bounds = Rect::from_wh(10.0, 10.0);

  {
    let mut rec = PictureRecorder::new();
    rec.begin_recording(bounds, None);

    for _ in 1..20_000{
      let snapshot = rec.finish_recording_as_picture(Some(&bounds)).unwrap();
      rec.begin_recording(bounds, None);

      let canvas = rec.recording_canvas().unwrap();
      canvas.draw_picture(&snapshot, None, None);
      canvas.draw_point((5.0,5.0), &paint);

      println!("{:?}", snapshot);
    }

    println!("will dealloc recorder...")
    // > thread 'tests::recorder_test' has overflowed its stack
    // > fatal runtime error: stack overflow
  }

  println!("done."); // never reached
}

And just to demonstrate that it’s related to the nested Picture drawing and not simply the number of drawing operations, this runs with no problem:

  fn many_dots_test() {
    use skia_safe::{Rect, PictureRecorder, Paint};
    let paint = Paint::default();
    let bounds = Rect::from_wh(10.0, 10.0);

    {
      let mut rec = PictureRecorder::new();
      rec.begin_recording(bounds, None);

      for _ in 1..900_000{
        let canvas = rec.recording_canvas().unwrap();
        canvas.draw_point((5.0,5.0), &paint);
      }

      let snapshot = rec.finish_recording_as_picture(Some(&bounds)).unwrap();
      println!("{:?}", snapshot);

      println!("will dealloc recorder...")
    }

    println!("done.");
  }
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

1 participant