microsoft / Lumia-imaging-sdk

Lumia Imaging SDK is a comprehensive set of GPU/CPU imaging tools and effects that run on both mobile and desktop, with high speed and memory efficiency. Samples and extras code are included.
MIT License
75 stars 37 forks source link

Consistent OutOfMemoryException when using 3.0 instead of 2.X #9

Closed jamesmundy closed 8 years ago

jamesmundy commented 8 years ago

I recently upgraded our solution from 2.X to 3.0.

In the old version we had the following code:

  using (var source = new StorageFileImageSource(sourceFile))
      {
        using (var effect = new FilterEffect(source) { Filters = new IFilter[] { new ScaleFilter(scaleFactor), new RotationFilter(angleForRotation) } })
        {
          using (var renderer = new JpegRenderer(effect))
          {
            renderer.Quality = 0.8;
            renderer.RemoveExif = true;
            var buffer = await renderer.RenderAsync();
            using (var output = await newFile.OpenAsync(FileAccessMode.ReadWrite))
            {
              await output.WriteAsync(buffer);
              await output.FlushAsync();
            }
            processed = true;
            return path;
          }
        }
      }

and in the new version this was replaced with the following:

  using (var source = new StorageFileImageSource(sourceFile))
      using (var scale = new ScaleEffect(source, scaleFactor))
      using (var rotation = scaleFactor < 1 ? new RotationEffect(scale, angleForRotation) : new RotationEffect(source, angleForRotation))
      using (var renderer = new JpegRenderer(rotation) { Quality = 0.8 })
      {
        var buffer = await renderer.RenderAsync();
        using (var output = await newFile.OpenAsync(FileAccessMode.ReadWrite))
        {
          await output.WriteAsync(buffer);
          await output.FlushAsync();
        }
        processed = true;
        return path;
      }

This code takes an image, scales and rotates it.

On a low end Windows Phone 8.1 device (Lumia 620) the new (3.0) consistently throws an OutOfMemoryException when performing this operation at RenderAsync. The old code works just fine with the same image.

Obviously being a low end device there are going to be memory restrictions but I think there must be something else going on given the first code works fine. To solve the issue I tried wrapping the code in a try/catch to capture the OOM exception and then try again with a smaller scale but this just kept on throwing OOM exceptions until a different generic error message appeared.

For the time being I have reverted to the old version of the SDK.

davidbozjak commented 8 years ago

The difference between SDK 2.X and 3.0 is that in the new version the SDK will use GPU rendering to optimize for time. A big difference between CPU and GPU processing is that, unfortunately, the GPU will require the entire source image to be decoded in memory at the same time. This offers large gains when it comes to the time it takes to render an image, however sometimes there won’t be enough memory to achieve that. The SDK does not try to estimate or guess if this will succeed, it is up to the developer to handle the exception.

If you have a scenario where this is happening you can force the SDK to revert to the CPU processing by setting the property RenderOptions on your renderer object. In your case renderer.RenderOptions = RenderOptions.CPU; and then calling renderer.RenderAsync() normally.

jamesmundy commented 8 years ago

Thanks David, this is useful information and helps a lot. I will set the rendering to be CPU on these low end devices.