Jump to content


Rendering continues forever/battery drain issues

Go to solution Solved by GreenSock,

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Hi guys,


Firstly, thanks for a great product. It didnt take more than a few hours to start converting my animation code to use GSAP and get my head around the flash-based concepts.


Im developing a webview application for Android and during testing (using the latest version of TweenMax.js from the CDN) Ive noticed that the battery drain was significantly higher than before I had started using GSAP. Now I know there could be other factors but after digging around using the GPU profiler on the Android device Ive noticed the following:


1) Just including the TweenLite or TweenMax JS includes causes approx 1 second of GPU usage on page load. This isnt the end of the world but it could significantly affect applications that reload the webview for whatever reason. This is before I even instantiate or run any tweens.


2) Setting up a simple TimeLine and adding a simple tween and playing it once using .play() the GPU will continue to tick even after the rendering is complete. This goes on indefinitely. A solution to this was to specify repeat:0 in the timeline options but im not sure why this isnt by default. This is probably the most serious bug as visually it would appear the render has completed but the GPU continues to burn battery life. In some cases when playing tweens in reverse, the repeat:0 fix above doesnt help. Ive found that adding .stop() to the oncomplete and onreversecomplete handlers has worked better.


3) If I specify an easing such as Expo.easeOut which seems to visually run faster than the duration Ive set, the GPU still continues to run even after the tween 'appears' to have completed. By this I mean once the tween has reached the target left/top property, it will still continue to run until the duration period ends. A simple check to see if the to target has been hit and abort rendering will stop this.


Ive only been using GSAP for a day so I may be doing something wrong as the above issues make it rather unusable on a mobile device. Im only performing simple tweens for window transitions, etc.. nothing too strenous and using the very basic left/top options.


Just quickly before hitting submit on this topic, Ive created a test in Chrome and viewed the timeline and its easily replicated on a browser. If you check the codepen, open timeline and start recording GPU events (rendering/painting) and click the box to start the tween you will see the events buffer keeps on filling up even after the tween has completed and even if the tab is running in the background. Now if you uncomment the lines code on lines #2 and #4, and do the same, you will see the events buffer stops a while after the tween finishes (around a second or so, roughly the same time as including the JS file causes).


Is there something I have missed? Perhaps having the oncomplete handler call a timeline.stoprendering or something similar? I suspect its due to calling requestanimationframe even when there is nothing to tween? But I could be wrong, I havent looked at the code at all. I will try to replicate this behaviour with Velocity.js and report back.


See the Pen dPKpVe by anon (@anon) on CodePen

Link to comment
Share on other sites

Hello mrz, and Welcome to the GreenSock Forum!


Thanks for your example.. very helpful!


I did have some other questions:

  • What Android version and Android version number are you testing on?
  • Are you using an actual android device or Google Chrome Mobile Emulator?
  • What version of Google Chrome are you seeing this on?
  • When you tested this in Chrome.. is this on windows, mac, or linux?

When testing performance in Chrome.. the Google Chrome Developers recommend to always test / debug in Incognito Mode to make sure your results do not get skewed by extensions and other running processes in Chrome.


Also i noticed you are animating the CSS left property instead of animating the x (translateX) property. The reason being is that the CSS left property does not animate on a sub-pixel level, only on a pixel level ( 98px ) .  But animating x property with GSAP you will be animating on a sub-pixel level ( 98.64858px ) .. which will make for a smoother animation.


You can learn more about why animating x and y are better than animating left and top.





Your example using x instead of left:


See the Pen emKdjv by anon (@anon) on CodePen


Does that help? :)

Link to comment
Share on other sites

Hi there,


The Android is a device, Galaxy Alpha running 4.4.4

Google Chrome is the latest Windows binary: 40.0.2214.115 m


I no longer think this issue is specific to mobile/android as it is easily reproduced in Chrome using the example. Im aware of the left/x difference, I chose left because I did not require subpixel precision and the animation itself is already very smooth. I dont have a problem with the speed/fluidity of the tween. Its just that it continues to eat up render cycles even after a tween has completed. This isnt such a big deal on a desktop browser but it is critical on a mobile/laptop  due to battery life.


Ive just tested a similar example with Velocity.js and it doesnt have the same issue. Unless Im doing something stupidly wrong it seems GSAP keeps rendering even when not necessary (ie tween has completed). Ive reverted back to CSS transitions for now as its much more efficient GPU-cycle wise.

Link to comment
Share on other sites

I think this is a glitch in the tools you're using to measure stuff (not your fault, of course). Here's why:

  1. I tested your theory using Chrome and was only able to reproduce it once. I've seen this sort of thing before in Chrome's Dev Tools where it spits out a bunch of events that report paints when I know for sure there's NOTHING happening. Zilch. Nada. Nothing to render. Nothing changing. I've reproduced it without even loading GSAP, so it wasn't a GSAP-specific thing. It was something really weird, like maybe if I loaded the page, then opened Dev Tools vs. having it open and then reloading...I can't remember, but it was clearly a glitch in the tooling when I looked at it a few months back. 
  2. Your comment about the "repeat:0" was another tip because I know for sure that is the default anyway. Setting it to zero the way you did is the same as not setting it at all. Feel free to look at the code and see for yourself. So if you were seeing intermittent different behavior by adding repeat:0, it strongly suggests isn't not a GSAP issue.
  3. Again, I tried uncommenting that stop() line in the onComplete and most of the time the paints stopped in Chrome even without that line. 
  4. There's nothing in GSAP that would possibly cause repaints like that when there's nothing actively tweening. Feel free to peruse the source and see for yourself.

Also note that Paul Irish (from the Chrome team) acknowledged that there are bugs with the way it reports (or doesn't report) CSS animations. I'd strongly recommend watching this video: http://greensock.com/css-performance/and reading the comments below. I fear that you may be drawing some faulty conclusions about performance based on faulty data that the tools are feeding you. Again, that's not your fault at all. I'd just encourage you to measure what matters in as real-world a way as possible. So instead of analyzing charts/graphs/numbers in Dev Tools, if your goal is performance that test various tools and see what actually looks smoothest. If your goal is to minimize battery usage, then maybe create a few versions with different tools, charge up your device and let it run for a certain extended amount of time and see where the battery status is after that. 


You said that Velocity doesn't have the issue and I have no reason to doubt that, but if your goal is top-notch performance, it may be worth running some more tests. I typically hear that people find GSAP to be faster - here's one article that has some charts: https://css-tricks.com/weighing-svg-animation-techniques-benchmarks/and then of course GSAP does a whole lot of things that other tools don't.


All that being said, I certainly don't blame you for trying to chase down what appears to be a battery-draining problem with GSAP. But like I said, I strongly suspect that the issue here isn't really a problem in GSAP, but rather some faulty metrics. I'm very eager to fix any problems in GSAP, of course, so please feed me any further data that points to issues in GSAP. We place a HUGE priority on performance. Hopefully you've picked up on that :) 


Thanks for taking the time to bring your concern to our attention and for providing details. 

  • Like 4
Link to comment
Share on other sites



Thanks for your reply - Ive definitely noticed that you guys place a huge priority on performance and thats exactly why Ive raised this issue. I had already watched your css performance screencast prior to posting this topic and Im aware the Chrome dev tools arent 100% accurate which is why I initially raised this issue on mobile only On Android devices you are able to profile GPU accurately and its a much better indicator of usage compared to dev tools.


I have run more tests since yesterday and GSAP definitely causes the device to run hotter/use more battery because the GPU keeps running even after tweens have ended. Velocity stops animating much earlier than GSAP. CSS transitions are the most efficient when it came to time spent animating. Just INCLUDING TweenMax.js caused the page to render for approx 1-2 seconds on load and this is easy to reproduce as it doesnt invoke any code on my end.


Regarding your point #2, it wasnt intermittent but I meant to say that the fix would only work for tweens going forward in the timeline. When playing in reverse repeat:0 didnt work. Adding stop() to the oncomplete and onreversecomplete handlers does work but as mentioned earlier there is an approx 1-2 second delay before it stops rendering.


I think the easiest way for you to see what I mean is to simply include the TweenMax.js on an android webview and enable GPU profiling and reload the page and see the usage. I can provide a demo APK if it makes it easier for you (requires android 4.3+ for GPU profiling)?  I will try and reproduce it using a normal website running on a mobile browser as well as that might be easier for you to test.


I will take a look at the source and let you know what I find.

Link to comment
Share on other sites

Hello again,


OK im making some progress. Ive looked at the source and added some logging and run a few tweens and noticed that you are right, it doesnt tween unless it has to.


And then I started looking at the ticker code which seemed fine, it would wake, tween and then sleep a little while later which is fine.


However.. in some cases, it has woken but not gone to sleep. requestAnimFrame continues to run in this time, generating all the 'traffic' in chrome timeline and this is what causes the ticking of the GPU on the mobile device.


I havent yet identified what would lead to a wake but no sleep but I think Ill figure it out soon.


edit: heres the log if it helps. you can see towards the end it wakes and runs 5000+ animation cycles (this continues to increment forever).

there is no animation happening on screen and the tween had ended long before (show drawer and hide drawer are the tweens).


also this shows you the 1-2 second of 'rendering' that occurs on page load.. there are 14 + 103 render cycles before I even interact with the page - just on loading the js...



  • Like 1
Link to comment
Share on other sites

Sorry to triple post but Ive managed to narrow down the steps to reproducing the problem as well as found a workaround that resolves 1 of the 2 issues I raised initially.


Steps to reproduce:

1) Create a timeline with paused:true and add a tween to the timeline, Im using a simple left:... tween set at 0.4 seconds long

2) Set an initial starting point for the timeline using: timeline.pause().progress(0.5);

3) timeline.play();


The expected result is that it will play until the end and stop because there is no repeat set. And you were right about repeat:0 it makes no difference if its set.


The actual result is that it will play and finish BUT the rendering starts over again and this time it doesnt run the tween it simply repeats the timeline duration without animating forever and never calls sleep




The workaround is nice and simple, you attach onComplete and onReverseComplete callbacks to the timeline and add timeline.pause() to both of them.


Now the timeline only renders once and doesnt eat up GPU cycles afterwards.


Now for the other issue.. If the tween is 120 cycles long but it visually 'finished' after 30 cycles of Animation._updateRoot, it still does the full cycle even if there is nothing left to tween which seems a bit wasteful.


On top of that, it does the 14+103 cycles on page load which also seems wasteful.


In short.. theres a bug where it doesnt stop rendering even after the tween is completed.


Ive also exposed the requestAnimFrame id via a global variable and called cancelAnimFrame on it from the onComplete() handler and it would 'interrupt' the Animation._updateRoot at 29 cycles! and then finish the remainder 92 cycles




So clearly the onComplete handler is being called at the correct time.. but for some reason rendering isnt stopped correctly. 26 cycles is great for mobile performance.. the other 92 wasted cycles not so much.. and the bug where it keeps rendering 119 cycles forever and ever just destroys the battery life.


I hope I provided enough information to fix the issue..

Link to comment
Share on other sites

First of all, thank you very much for all the detailed information. Very helpful. 


I have tried many, many times to follow your directions and I can't reproduce the problem even one time. The ticker sleeps properly every single time. I re-read your directions and made sure I was following them...no dice. 


Can anyone else reproduce this? 


A few questions:

  1. You're logging in the latest version of Chrome, right? (on a desktop)
  2. What operating system?
  3. What version of GSAP are you using? You can console.log(TweenLite.version).

Also, to clarify the "extra" cycles that look wasteful to you, there's actually a design to the system, and believe it or not. When we initially load the scripts, we have to check a few things to work around bugs in various browsers. For example, iOS had a bug that caused requestAnimationFrame cycles to simply stop if you switched tabs and then came back. We use a setTimeout() to check after 1.5 seconds and compare how many cycles there have been and gauge if the bug is present, and then react accordingly. In short, it's protecting you from having your animations just stop in certain browsers due to bugs in those browsers. It's not processor-intensive at all - just some cheap requestAnimationFrame ticks. 


The reason we don't immediately stop the _updateRoot() loop when a tween finishes is because it's actually more efficient in several ways. We check every 120 ticks and perform some GC tasks and then if there aren't any tweens left running, we power down. That avoids having to constantly check every time a tween finishes, and it also avoids a bunch of wake/sleep/wake/sleep routines when the user/app may be active.


Think of it like a sports car...when you pull up to a stop light, you don't turn your car off to save gas. You leave it idling so that you can press the gas as soon as the light turns green, and get results quick. Likewise, with GSAP we leave things idling for up to a maximum of about 2 seconds so that you can get excellent responsiveness when you need it, but if you don't do any tweening for that amount of time, chances are it's safe to "power down". And let's face it: calling _updateRoot() when there aren't any tweens anyway is not very taxing at all. It's dirt cheap. But I totally agree with you that we don't want to just let it run forever which is why we have the 2-second cap (usually it's less than that anyway). 


I noticed you keep talking about the "GPU" being active, and that's another puzzling thing for me because the JS tasks we're talking about are CPU-bound, not GPU. If we were actually moving pixels around, updating transforms or opacity or whatever, I could see the GPU getting involved. But for a simple JS function that does basically nothing, don't you find it curious that Chrome (or Android) is reporting a bunch of GPU/rendering tasks? Hopefully you can see in the code that there's absolutely nothing that'd cause that from GSAP. That's another reason why I think this may be a tooling issue (faulty reporting). But I still can't reproduce it even a single time today using your directions, so I'm kinda baffled and hoping you might have some other tips. 

Link to comment
Share on other sites

It seems that anytime you call requestAnimFrame it hits the GPU (atleast on a mobile device). On chrome it presents itself as those 'ghost' paints/renders that have no source.


I agree with the analogy of the sports car and having the ticker keep running and stay 'warm' and this is probably perfectly fine on a desktop pc. The thing is each 1 of those ticker cycles on a mobile is 0.01% battery life or whatever it may be. Like I mentioned earlier, I have been testing my app with several libraries and GSAP noticeably used battery by atleast 50% more during heavy testing. I would normally get a good day of testing on a single charge and when I swapped to GSAP it reduced to less than half - its obvious this was due to the 'wasted' cycles.


Yep - latest Chrome version as mentioned above, running Windows 7 using the latest GSAP from the CDN. Tried the github version but no difference.. using the uncompressed one right now so I can mess with the source.


I will create a codepen for you now, I think I can get it to reproduce reliably.

Link to comment
Share on other sites

Yikes, it sounds like the Android browsers are a mess if they force the GPU to get involved on every requestAnimationFrame and they eat up a ton of battery just from doing some basic function calls like that. Frankly, in all my years doing this and with all the big-name companies using GSAP, this is the first I've heard any claim like that regarding battery usage. It's tough to wrap my head around how a browser could be that wasteful and I wonder if it's device-specific or at least Android-specific. 


We could certainly change the 120 tick number to be lower on mobile or something. But again, it's a balancing act - the more often we run it, the more processing is required. 


I'm curious - did it make any difference if you did NOT pause the timeline? I'm trying to whittle down the variables involved here and what exactly tends to trigger the problem for you. Is it only with timelines? What if you did a plain TweenLite tween? Do you still see the same issue? Do you have to jump to a middle-point, like progress(0.5)? Does it still happen if you run it from the beginning normally? 

Link to comment
Share on other sites

To be honest the entire range of mobile webviews are a joke.. so many inconsistencies and bugs but on the plus side it forces you to optimize as much as possible.


You are right, pause is not necessary ive just tried it without. And it runs fine if you start from the beginning normally

Anyway, Ive just gone through my steps and it seems I missed the final step! Sorry about that:


1) set up a paused timeline

2) add a tween

3) set progress somewhere in middle (doesnt have to be 0.5)

4) play the timeline

5) reverse the timeline!


Everything is 100% fine up to step 5. Once you hit reverse it will start rendering 120 cycles indefinitely.


Im trying to get this working in a codepen now, let me know if you have any luck


edit: And its probably never come up before because I would assume most of the GSAP users would animate more than 120 cycles and probably dont mind having the renderer eating CPU/GPU in the background. For my purposes I only do animations that need 30 cycles or so and Im trying to optimize it as best i can.

Link to comment
Share on other sites

Yeah, I totally understand your desire to optimize things for your scenario and minimize battery drain. No worries. 


And yes, I was able to reproduce this if I reverse() a timeline that had already been reversed and finished. That seems to be the key. It's flagging the timeline as eligible for rendering (removed from GC queue) but since it's already finished, it doesn't render again anyway, which is what would run the logic that tells it to go back into the GC queue (when it's done). I'm working on a fix now. Thanks for sticking with this. :)

  • Like 2
Link to comment
Share on other sites

Awesome, glad you could find it.


For the second issue, do you think it would be feasible to have a flag where we could request GC to run at the end of tween, therefore saving the wasted cycles? It would be useful in the mobile scenario so after the 20-30 cycles once onComplete() is triggered, it would run the GC and cancelAnimFrame. Or maybe expose it via TweenLite.gc() or something and let us call it manually


And if it was possible to set some sort of flag, e.g. TweenLite.iOS6Fix to allow us to avoid the 1.5 second rendering on page load. Most mobile users will be able to identify the target platform fairly accurately and it just seems to be a waste to have all platforms suffer at Apple's expense :)


Thanks for your help! Let me know once there is a fix and Ill try and reproduce the issue again. Im not able to get it to reproduce in codepen for some reason :(


edit managed to get it working in the codepen: 

See the Pen gbKvXa by anon (@anon) on CodePen

sorry for the messy code - its quite possibly the worst example ever made

Link to comment
Share on other sites

Alright, I believe I resolved the issue, and it only would happen if a reversed timeline finished and then you tried to reverse it again (or resume()). I attached a preview of what I plan to release in the next day or two. 


You can also check out the latest beta of the uncompressed version of TweenMax at https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/TweenMax-latest-beta.js(just for testing please)


If you'd like to reduce the frames between GC cycles (which is when it checks and puts the ticker to sleep if necessary), you can set:

TweenLite.autoSleep = 30; //or whatever number (the default is 120)

Let me know if that helps at all. My plan is to release the update in the next day or so; if you can kick the tires a bit and provide feedback ASAP, that'd be swell. 


  • Like 2
Link to comment
Share on other sites

Thanks a lot! Thats fixed the issue, Ive tried to reproduce it and have not been able to.


I have one question though, the autoSleep timer is definitely very helpful except in one scenario. If i use set the progress() on a timeline and pause it to jump to a specific point in the timeline, it will use the full # of frames I set before triggering GC.


Is there any way for me to force GC to occur at points where I think it should happen? This would allow me to basically call them in between window transitions when I know there wont be a tween running and also allows me to be more flexible if the tween was longer/shorter than 30 frames or whatever.


Ive tried calling TweenLite.ticker.sleep() and while this works initially, it just wakes up again straight after.


edit: I say GC but I really just want to cancelAnimFrame because thats what triggers the GPU on android

Link to comment
Share on other sites

OK Ive just spent the past hour testing it on Android with the GPU profiler with the old/new GSAP and also against CSS transitions and Im very pleased with the results!


It seems you must have fixed the issue where it would do a full 120 frames for every tween because now it stops using the GPU once the tween ends, no need for me to specify autoSleep or to even have oncomplete() handlers anymore!


Ive taken some screenshots for you to see how much better it is now. Notice its also ~2x faster framerate and also uses less GPU time than CSS transitions! Now the only thing left is to get rid/reduce the tiny spikes at the beginning of each tween but I think this is probably unavoiable due to not having the ticker 'warmed up'. Anyway its not causing any noticeable issues so probably not even worth investigating.


In the graphs below, each spike indicates the start of a tween/transition. The green line at the top indicates max time to hit 60fps, below that is what we want. All 3 run with transform3d so its an even playing field.


1) GSAP 1.15.1 (minified). There is only 1 peak because each tween would take 120 frames. This would waste battery as GPU was used for ~6x longer than needed.



2) GSAP 1.15.2 (uncompressed). 6 peaks = 6 tweens = 5x more tweens in the same time as the old GSAP.



3) CSS transitions (notice they take ~1.5x longer to render for the same duration/easing and also ~2x slower framerate). 4 peaks = 4 tweens.



Very happy with the above :) Thanks for resolving it so quickly. I can now convert all the animations to use GSAP and will continue testing on other platforms/devices to see if the above performance improvement is consistent.

  • Like 2
Link to comment
Share on other sites

That's EXCELLENT news, mrz. Thanks for sharing the details. Quite encouraging. You get bonus points for the visuals :)


As for the spikes when a tween starts, that doesn't surprise me at all and like you said, it's probably not even noticeable in "real-world" scenarios, but allow me to explain...


The most processing-intensive part of a tween's lifecycle is when it renders for the FIRST TIME because that's when it has to query the DOM (or whatever you're tweening) to ascertain the current values, and read the values you're animating too, and calculate the difference so that it can be super-optimized DURING the tween. That's a one-time cost for the tween, so if you restart() it later, for example, it doesn't have to do that again - it uses the values it recorded already. 


Again, this is all centered around ensuring that the animation "feels" smooth while it's playing. It's much better to take a small hit up-front on the first render so that all subsequent renders are cheap. The spike you saw probably wasn't all about waking the ticker back up again; it's likely querying the DOM for the current values and setting up the optimized tween. 


Also keep in mind that GSAP doesn't do this work until the the first render of the tween. So, for example, if you have a tween with a delay of 2 seconds, it won't do that instantiation work until that time (it doesn't happen when you create the tween). 


If you want to reduce the spikes, one thing you could consider doing (and this isn't always wise) is to take that hit when your app first loads by forcing your tweens/timelines to their end, and then immediately going back to the beginning. Imagine 50 tweens sequenced in a timeline, each lasting 1 second. Normally, you'd see a small spike ever second, as each tween starts. However, if you did timeline.progress(1).progress(0), it'd force the playhead to the very end which would make everything instantiate and do its recordings NOW, and then go right back to the beginning and play from there, so the user would never actually see it go to the end initially. You'd take all those instantiation spikes and get them over with up front, but of course that means a higher initial spike. Thereafter, the timeline would play with minimal spikes. 


That technique isn't always viable because plenty of times your animations will be totally dynamic (maybe you tween to wherever the user clicks or some other highly dynamic variable), so pre-calculating can't be done. Or sometimes you really need to avoid any initial slowdowns and get the app running ASAP, so the up-front cost isn't attractive. But in some cases, it's a really handy technique. 


Let us know how things go as you move things over to GSAP. Please note that due to the pretty massive amount of changes and new features in the update, we called in 1.16.0 instead of 1.15.2 and it's officially released now :)


Thanks again for your help in identifying that issue. It made GSAP better, that's for sure. 

  • Like 2
Link to comment
Share on other sites

Thanks for the tip, while trying to implement it I came across another bug for you :)


If you play() a timeline until finish or set progress(1) and then play(), it will wake and never sleep.. So basically when a timeline has played until end and you try to play it again.


Ive updated the codepen to demonstrate the above, the bug is in both 1.15.1 and 1.16.0 - 

See the Pen OPEKgr by anon (@anon) on CodePen


Shame I didnt catch this before you released the new version! Are there any other scenarios where this might happen? I did run into one during testing but I was not able to replicate it.. it may or may not be the same bug.. but it seems there are very rare cases where the ticker continues to tick and ignore the sleep value of 30 or 120 or whatever it may be.

Link to comment
Share on other sites

Hi mrz :)


pls try .play(0) or .restart() 

Link to comment
Share on other sites

Hi Diaco,


Thanks - Ive put a workaround in for now. But the issue remains that the ticker might not sleep in some cases which causes the battery drain issues mentioned. Im sure its better to make the ticker/wake/sleep logic bulletproof rather than resort to workarounds - there should never be a case where the ticker doesnt sleep :)

Link to comment
Share on other sites

I'll look into this soon. Stand by...

Link to comment
Share on other sites

  • Solution

Alright, I found exactly what the problem was and it would indeed only happen in cases where you resumed an already-completed tween/timeline AND you leave it completed (meaning you don't move the playhead at all). I believe it is resolved in the attached preview of 1.16.1. Please take a peek and let me know if things work well for you. It passed all my tests with flying colors :)


Again, I really appreciate your diligence and patience on this. 


  • Like 2
Link to comment
Share on other sites

Hello again,


Ive just tested it and it seems to be working well. Thanks for looking into both issues - will let you know if I find any more :)

Link to comment
Share on other sites

That's great, mrz. Thanks for confirming. I hope to release this update in the next couple of days. 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.