locked
Storyboard - Moving large Bitmap always choppy?? RRS feed

  • Question

  • Hi everybody,

    I have developed a wpf storyboard-based program which is mainly animating DrawingVisuals around on screen (VS 2010/.Net framework4 client Profile). My development machine is an Intel Core 2 Duo (2,66 Ghz) and i have an NVIDIA GeForce 7600 GS in it.

    When my application is rendered to a resolution of 1024 x 768 everything is fine. But it is sometimes really choppy on 1920 x 1200. So I made a very simple testapp which only moves 1 DrawingVisual displaying a BitmapImage which fills the whole window from right to left through the screen in a loop. I use a storyboard with a DoubleAnimation of Canvas.LeftProperty therefore. What happens is that the picture starts to move very smooth into the screen until 3/4 of it is visible and then it becomes really choppy until ~1/4 of the bitmap has moved out of the screen, then its smooth again.

    CPU usage is constantly betwen 0 and 1%, so why the ____ is this thing so laggy? I set the Threadpriority of my app to maximum and adjusted a desired framerate of 60 what actually didn`t make anything better.

    Dispatcher.Thread.Priority = System.Threading.ThreadPriority.Highest; 
    
    Timeline.DesiredFrameRateProperty.OverrideMetadata(typeof(Timeline), new FrameworkPropertyMetadata { DefaultValue = 60 }); 
    
    

    Afterwards i installed the wpf performance suite and got the perforator-tool running (the visual profiler does not display anything, don`t know why). The perforator shows that my framerate is toggling between 50 and 65 frames/sec and video memory usage is totally constant at 19MB.

    So why the ____ is this thing so choppy, this is really driving me nuts and don`t have any idea what i could do to get this smooth. It would be very cool if anyone here could help me!

     

    edit: I have tried the following animation types, the result is always the same:

     - Storyboard

     - Local Animation

     - Animation Per Frame (hooked into CompositionTarget.Rendering event)

    Seems to me as this laggy behavior comes from somewhere inside the wpf-rendering engine itself ??? :-(

    tried on different PC too with exactly same result 

    Thursday, June 24, 2010 8:18 AM

All replies

  • Hi Xifried,

    Use Timeline.DesiredFrameRate property to define a specific frame rate at which animations should run is just a guideline only, and is not a guaranteed value. This property has two primary uses:
    1. Limiting the amount of processing resources for slow-moving, background type animations that do not require a high degree of fidelity. This can be achieved by setting a small frame rate value on the timeline.
    2. Reducing the visual impact of tearing on fast-moving horizontal animations. This can be achieved by setting a high frame rate value on the timeline.
    For more information please refer the link as follows:
    http://msdn.microsoft.com/en-us/library/system.windows.media.animation.timeline.desiredframerate.aspx

    It would great that if you can provided a simple but complete sample that could reproduce the issue, so that we can have a better understanding about it.

    Best regards,
    Kevin Pan


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Tuesday, June 29, 2010 2:26 AM
  • As I have already said in my first post, i set the desiredFramerate property to 60 (which should result in a smooth animation). I even tried other values but without a proper effect.

    Ok, here comes the code.

    1.) Create a new wpf-project. Add a folder with the name "Image" to your Project-directory (on the level of the "bin"-folder)

    2.) Then put a large image (my screen has resolution 1900 x 1200 so my image has the same size) into this folder and name it "TestScreen.png". (If you prefer using a .bmp or a .jpg you can do that, you simple have to change the line of code were the image is loaded). You must try this on a screen with a high reolution, the effect will not happen on a resolution of 1024x768!

    3.) Then you have to add references to "System.Windows.Forms" and "System.Drawing" to the project.

    4.) Copy/Paste the code for MainWindow.xaml and MainWindow.xaml.cs and compile

    <Window x:Class="ChoppyAnimation.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Choppy Animation" Height="768" Width="1024" Loaded="Window_Loaded" Cursor="None" AllowsTransparency="False" Closing="Window_Closing">
     <Canvas x:Name="canvasRoot" Background="Gray">
     </Canvas>
    </Window>

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Windows.Media.Animation;
    using System.Windows.Forms;
    
    namespace ChoppyAnimation
    {
     /// <summary>
     /// Interaktionslogik für MainWindow.xaml
     /// </summary>
     public partial class MainWindow : Window
     {
      private static string STRING_STARTUP = AppDomain.CurrentDomain.BaseDirectory.Replace("\\bin", "").Replace("\\Debug", "").Replace("\\Release", ""); // start-up-path of the application
      private Storyboard _story;
      private Image _img;
    
      public MainWindow()
      {
       InitializeComponent();
    
       Dispatcher.Thread.Priority = System.Threading.ThreadPriority.Highest;
       Timeline.DesiredFrameRateProperty.OverrideMetadata(typeof(Timeline), new FrameworkPropertyMetadata { DefaultValue = 60 });
      }
    
    
      private void Window_Loaded(object sender, RoutedEventArgs e)
      {
       // turn on for SOFTWARERENDERING
       /*HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
       HwndTarget hwndTarget = hwndSource.CompositionTarget;
       hwndTarget.RenderMode = RenderMode.SoftwareOnly;*/
    
       // ---------- Here we move the window to a specific screen ------------
       WindowStyle = WindowStyle.None;
       int iScreenNum = 0; // number of the screen on which our app shall be displayed
       Screen targetScreen = null;
       if (Screen.AllScreens.Length > iScreenNum)
        targetScreen = Screen.AllScreens[iScreenNum];
       else
       {
        targetScreen = Screen.PrimaryScreen;
        iScreenNum = 0;
       }
    
       WindowState = WindowState.Normal;
       this.Left = targetScreen.Bounds.Left;
       this.Top = targetScreen.Bounds.Top;
       this.Width = targetScreen.Bounds.Width;
       this.Height = targetScreen.Bounds.Height;
       canvasRoot.Width = Width;
       canvasRoot.Height = Height;
       ResizeMode = System.Windows.ResizeMode.NoResize;
       // --------------------------------------------------------------------
    
       // Here we create the image-ctrl which we will move from right to left through the screen
       _img = new Image();
       _img.Width = this.ActualWidth;
       _img.Height = this.ActualHeight;
       _img.Stretch = Stretch.Fill;
       _img.Source = GetBitmapSource("TestScreen.png");
       _img.Source.Freeze();
       _img.SetValue(Canvas.LeftProperty, this.Width);
       canvasRoot.Children.Add(_img);
       
       // here we create the storyboard and start our animation
       StartStoryboard(); 
      }
    
    
      public static ImageSource GetImageSource(string filename, double imageWidth, double imageHeight)
      {
       return GetImageSource(filename, Convert.ToInt32(Math.Round(imageWidth, 0)), Convert.ToInt32(Math.Round(imageHeight, 0)));
      }
    
      public static ImageSource GetImageSource(string fileName, int imageWidth, int imageHeight)
      {
       BitmapImage bi = new BitmapImage();
       bi.BeginInit();
       bi.UriSource = new Uri(STRING_STARTUP + @"\Image\" + fileName, UriKind.Absolute);
       bi.DecodePixelWidth = imageWidth;
       bi.DecodePixelHeight = imageHeight;
       bi.EndInit();
       return bi;
      }
    
      public static BitmapSource GetBitmapSource(string fileName)
      {
       Uri uri = new Uri(STRING_STARTUP + @"\Image\" + fileName, UriKind.Absolute);
       PngBitmapDecoder decoder = new PngBitmapDecoder(uri, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
       BitmapSource bitmapSource = decoder.Frames[0];
       return bitmapSource;
      }
    
    
      private void StartStoryboard()
      {
       _story = new Storyboard();
       _story.SetValue(Storyboard.DesiredFrameRateProperty, 60);
       _story.RepeatBehavior = RepeatBehavior.Forever;
       DoubleAnimation daMove = new DoubleAnimation(Width, -Width, TimeSpan.FromMilliseconds(4000));
       Storyboard.SetTargetProperty(daMove, new PropertyPath(Canvas.LeftProperty));
       Storyboard.SetTarget(daMove, _img);
       _story.Children.Add(daMove);
       _story.Freeze();
       _story.Begin();
      }
    
      private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
      {
       if (_story != null)
        _story.Stop();
      }
     }
    }

     

    Tuesday, June 29, 2010 8:22 AM
  • Hmmm...... no solution in sight. :-((

    Has at least someone tried the code above until now?

    Thursday, July 8, 2010 3:48 PM