Jump to content

  • Log In with Google      Sign In   
  • Create Account

We need your help!

We need 7 developers from Canada and 18 more from Australia to help us complete a research survey.

Support our site by taking a quick sponsored survey and win a chance at a $50 Amazon gift card. Click here to get started!


comfy chair

Member Since 05 Jan 2011
Offline Last Active Aug 29 2015 12:49 PM

#5236150 Animation takes up too much memory

Posted by comfy chair on 22 June 2015 - 07:15 AM

With the changes to the struct (uint instead of long, vectors instead of Matrix) I got down to 28 MB. That's good enough for now I think. 

 

Thanks for the help.




#5235773 Animation takes up too much memory

Posted by comfy chair on 19 June 2015 - 04:14 PM

Yes, I should of course do that when possible. The build time is long enough already.




#5235731 Animation takes up too much memory

Posted by comfy chair on 19 June 2015 - 12:04 PM

Yes, I already made sure that I use the same interpolation method in preprocessing and at runtime. SLERP was necessary to use because LERP sometimes gave crazy errors.

 

I asked the animator if we ever used scaling, the answer was "don't know". But now I think it will probably be needed at some point, if it isn't being used already.




#5235705 Animation takes up too much memory

Posted by comfy chair on 19 June 2015 - 09:48 AM

So I need to choose if I want to support scaling, or not. Actually, I don't think I will ever need it.




#5235687 Animation takes up too much memory

Posted by comfy chair on 19 June 2015 - 08:35 AM

 

 

"Redundant frames" needn't mean only frames that are the same. A redundant frame is any frame that is sufficiently close to the value you would calculate by interpolating/SLERPing the previous and following frame. I.e., if you're interpolating frames anyway, and the frame data for time t is what you get from interpolating frame t-1 and frame t+2, frame t is redundant.

 

I eliminated these frames also and I am now down to 44 MB. Perhaps I can compress it a bit further by increasing the comparison threshold without hurting the playback quality.




#5235562 Animation takes up too much memory

Posted by comfy chair on 18 June 2015 - 03:29 PM

Alright then. But L. Spiro used the same term...


I am trying to replace this, inside BoneKeyFrame:

/// <summary>
/// The transform for the keyframe.
/// </summary>
public readonly Matrix Transform;

with a custom class/struct. It should contain an optional translation vector, optional scale vector (when different from 1) and a rotation quaternion. 
How can I design it so it becomes as compact as possible...  The point is to reduce the size of the
BoneKeyFrame array.

 




#5235553 Animation takes up too much memory

Posted by comfy chair on 18 June 2015 - 02:58 PM

I found some useful information here:

http://www.digitalrune.com/Support/Blog/tabid/719/EntryId/131/Character-Animation-Compression.aspx




#5235543 Animation takes up too much memory

Posted by comfy chair on 18 June 2015 - 02:06 PM

I have eliminated the redundant key frames (frames that have the same transform matrix as the preceding one) and have now reduced the size to 84 mb.

 

I am wondering if there are more low-hanging fruits that can bring it down further....

 

 

The vast majority of animation data needs only rotation data (i.e., no scaling or translation). If that's stored as a quaternion, that's 4 float values. Say 30 bones, 3 quaternions each, 4 floats per quaternion, 4 bytes per float value = 1.5K per animation, with the possibility that could be shared among any characters using the same frame hierarchy.

 

 

Yes, what I have is 4*4 matrices, likely quaternions. If I decompose them into vectors, I would need to change the runtime animation code to use vectors instead... I am not sure if I want to do that at this point...

 

This is what the struct looks like:

 /// <summary>
    /// Represents a keyframe in an animation track.
    /// </summary>
    public struct BoneKeyframe
    {
        /// <summary>
        /// Creats a new BoneKeyframe.
        /// </summary>
        /// <param name="transform">The transform for the keyframe.</param>
        /// <param name="time">The time in ticks for the keyframe.</param>
        public BoneKeyframe(Matrix transform, long time)
        {
            this.Transform = transform;
            this.Time = time;
        }
        /// <summary>
        /// The transform for the keyframe.
        /// </summary>
        public readonly Matrix Transform;
        /// <summary>
        /// The time for the keyframe.
        /// </summary>
        public readonly long Time;

    }

Most of the keyframes are just hanging around in memory. How could I compress it until the transform is needed..?

 

 

 

 

As L. Spiro mentions, eliminate redundant frame data from the data you're importing (not at runtime).
If you have 20 frames of animation data that are equivalent (or close enough) to LERP'ing or SLERP'ing between frames 0 and 19,
you only need 2 keyframes per bone.

 

 

I have already eliminated the frames that are equal. But is the situation really that common in animation that a handmade animation sequence is similar to a SLERP? How would I detect this case. How do I select the endpoints in the animation sequence to compare, when doing the loop I posted above?
 




#5235533 Animation takes up too much memory

Posted by comfy chair on 18 June 2015 - 12:02 PM

I reduced the sampling rate to 30 frames per second from 60. The memory used went down from 206 mb to 108 mb, while animation quality looks the same.

I will try to reduce it a bit further, then I will probably try removing the redundant key frames as was suggested.
 




#5235522 Animation takes up too much memory

Posted by comfy chair on 18 June 2015 - 10:24 AM

 


I may be able to reduce this data and still get smooth playback.

 

If your code (properly) interpolates animation data at runtime, do you need to do anything other than just load the data? I.e., game assets in general should be "ready to load" at runtime.

 

 

The code I posted only runs at build time. It creates the key frame arrays that the game interpolates between during runtime.
My problem was that these arrays were so huge.




#5235518 Animation takes up too much memory

Posted by comfy chair on 18 June 2015 - 10:15 AM

else // Else the adjacent frames have identical transforms and we can use
// the current frames transform for the current keyframe.
{
keyframe = new AnimationKeyframe(new TimeSpan(time),
channel[currentFrame].Transform);
}

Here, I might as well not store any keyframe at all. I wonder how much data could be saved that way.




#5235509 Animation takes up too much memory

Posted by comfy chair on 18 June 2015 - 09:52 AM

As a quick hack, I'd setup that the animation to take just 10% of the time and play it 10 times slower. If that reduces the size and still looks good, then there is really a sampling issue with the exporter as you're assuming.

Yes, but the code i posted above suggests that everything that gets exported is overridden in a resampling loop.




#5235500 Animation takes up too much memory

Posted by comfy chair on 18 June 2015 - 09:31 AM

I am looking at the code that pre-compiles the animation data coming from the exporter.

It looks like it is storing 60 keys per second, regardless of what it receives from the exporter.

Here is the code:
 


                // Step through time until the time passes the animation duration
                while (time <= animationDuration)
                {
                    AnimationKeyframe keyframe;
                    // Clamp the time to the duration of the animation and make this 
                    // keyframe equal to the last animation frame.
                    if (time >= animationDuration)
                    {
                        time = animationDuration;
                        keyframe = new AnimationKeyframe(new TimeSpan(time),
                            channel[channel.Count - 1].Transform);
                    }
                    else
                    {
                        // If the channel only has one keyframe, set the transform for the current time
                        // to that keyframes transform
                        if (channel.Count == 1 || time < channel[0].Time.Ticks)
                        {
                            keyframe = new AnimationKeyframe(new TimeSpan(time), channel[0].Transform);
                        }
                        // If the current track duration is less than the animation duration,
                        // use the last transform in the track once the time surpasses the duration
                        else if (channel[channel.Count - 1].Time.Ticks <= time)
                        {
                            keyframe = new AnimationKeyframe(new TimeSpan(time), channel[channel.Count - 1].Transform);
                        }
                        else // proceed as normal
                        {
                            // Go to the next frame that is less than the current time
                            while (channel[currentFrame + 1].Time.Ticks < time)
                            {
                                currentFrame++;
                            }
                            // Numerator of the interpolation factor
                            double interpNumerator = (double)(time - channel[currentFrame].Time.Ticks);
                            // Denominator of the interpolation factor
                            double interpDenom = (double)(channel[currentFrame + 1].Time.Ticks - channel[currentFrame].Time.Ticks);
                            // The interpolation factor, or amount to interpolate between the current
                            // and next frame
                            double interpAmount = interpNumerator / interpDenom;
                            
                            // If the frames are roughly 60 frames per second apart, use linear interpolation
                            if (channel[currentFrame + 1].Time.Ticks - channel[currentFrame].Time.Ticks
                                <= ContentUtil.TICKS_PER_60FPS * 1.05)
                            {
                              //  context.Logger.LogImportantMessage("Lerp between frames {0} and {1}, interpAmount: {2}", currentFrame.ToString(), (currentFrame + 1).ToString(), interpAmount.ToString()); //  input.Duration.Ticks.ToString(), ((long)(durationFactor * input.Duration.Ticks)).ToString());

                                keyframe = new AnimationKeyframe(new TimeSpan(time),
                                    Matrix.Lerp(
                                    channel[currentFrame].Transform,
                                    channel[currentFrame + 1].Transform,
                                    (float)interpAmount));
                            }
                            else // else if the transforms between the current frame and the next aren't identical
                                 // decompose the matrix and interpolate the rotation separately
                                if (channel[currentFrame].Transform != channel[currentFrame + 1].Transform)
                            {
                            //    context.Logger.LogImportantMessage("Slerp between frames {0} and {1}, interpAmount: {2}", currentFrame.ToString(), (currentFrame + 1).ToString(), interpAmount.ToString()); //  input.Duration.Ticks.ToString(), ((long)(durationFactor * input.Duration.Ticks)).ToString());

                                keyframe = new AnimationKeyframe(new TimeSpan(time),
                                    ContentUtil.SlerpMatrix(
                                    channel[currentFrame].Transform,
                                    channel[currentFrame + 1].Transform,
                                    (float)interpAmount));
                            }
                            else // Else the adjacent frames have identical transforms and we can use
                                    // the current frames transform for the current keyframe.
                            {
                                keyframe = new AnimationKeyframe(new TimeSpan(time),
                                    channel[currentFrame].Transform);
                            }

                        }
                    }
                    // Add the interpolated keyframe to the new channel.
                    outChannel.Add(keyframe);
                    // Step the time forward by 1/60th of a second
                    time += ContentUtil.TICKS_PER_60FPS;
                }

But at runtime, interpolation is performed again. So as some of you mentioned, I may be able to reduce this data and still get smooth playback.




#5235464 Animation takes up too much memory

Posted by comfy chair on 18 June 2015 - 07:20 AM

When we were setting up the pipeline, we found that there was only one specific way to get the animations to play properly in the game. It involved baking all the keyframes. I am not sure if we will be able to tweak the export procedure very much without it breaking.




#5235458 Animation takes up too much memory

Posted by comfy chair on 18 June 2015 - 07:14 AM

I was wrong. I have looked at the memory profile and it says that the animation data is not copied for each character instance as I thought.
However, the game still uses over 200 mb of memory for animations alone, and I would like to reduce that if possible.






PARTNERS