XNA UK User Group

A helping hand for bedroom coders throughout the land.
in

This Blog

Syndication

Twitters

RandomChaos

August 2008 - Posts

  • Generic XNA - Threading for Windows & XBox 360

    OK, I went back to my old threading code to port it to the 360 so I could start playing about with processor affinity (as promised last time) and found the compact framework for the 360 was a bit thin on the ground for parameterized threading, so I have re written the whole thing and it will now play on both the PC and the 360 and also included processor affinity for your 360 projects and there is very little interface change so if you were using the code for your Windows games you can just replace the threading code wit this and you wont have to alter any of your calls to it.

    So, what did I do??

    Pretty much ripped the lot out and started again. The ThreadManagers innards have been totally re done, now instead of having a shed load of Dictionaries holding mutex's and the like we now just have three, one for the thread starters, one for the threaded code and one for the threads them selves.

        /// <summary>
        /// This is the Thread manager.
        /// </summary>
        public class ThreadManager : GameComponent
        {
    
            /// <summary>
            /// List of ThreadStart used to start the treads.
            /// </summary>
            private Dictionary<int, ThreadStart> threadStarters = new Dictionary<int, ThreadStart>();
            private Dictionary<int, ThreadCodeObj> threadedCodeList = new Dictionary<int, ThreadCodeObj>();
            /// <summary>
            /// List of runnign threads.
            /// </summary>
            private Dictionary<int, Thread> threads = new Dictionary<int, Thread>();
    
            /// <summary>
            /// Managers GameTime to be passed onto the threads.
            /// </summary>
            static GameTime gameTime;
            /// <summary>
            /// ctor
            /// </summary>
            /// <param name="game">Calling game class</param>
            public ThreadManager(Game game) : base(game)
            { }
    
            /// <summary>
            /// Overiden Update call, loads manager gameTime varaible.
            /// </summary>
            /// <param name="gameTime"></param>
            public override void Update(GameTime gameTime)
            {
                ThreadManager.gameTime = gameTime;
    
                for (int t = 0; t < threadedCodeList.Count; t++)
                    threadedCodeList[t].Update(gameTime);
    
                base.Update(gameTime);
            }
    
            /// <summary>
            /// Method to add a thread to the maanger.
            /// </summary>
            /// <param name="threadCode">Code to be executed in the thread.</param>
            /// <param name="threadInterval">Time period between each call in miliseconds</param>
            /// <returns>Index of thread, first one added will be 0 next 1 etc..</returns>
    #if XBOX
            public int AddThread(ThreadCode threadCode, int threadInterval,int affinityIndex)
    #else
            public int AddThread(ThreadCode threadCode, int threadInterval)
    #endif
            {
                int retVal = threads.Count;
    
    #if XBOX
                ThreadCodeObj thisThread = new ThreadCodeObj(threadCode, threadInterval,affinityIndex);
    #else
                ThreadCodeObj thisThread = new ThreadCodeObj(threadCode, threadInterval);
    #endif
    
                threadedCodeList.Add(threadedCodeList.Count, thisThread);
                threadStarters.Add(threadStarters.Count, new ThreadStart(thisThread.Worker));
                threads.Add(threads.Count, new Thread(threadStarters[threads.Count]));
    
                threads[threads.Count - 1].Start();
    
                return retVal;
            }
    
            /// <summary>
            /// Method to kill a single thread.
            /// </summary>
            /// <param name="index"></param>
            public void KillThread(int index)
            {
                threadedCodeList[index].KillThread(threads[index]);
            }
    
            /// <summary>
            /// Method to start a thread
            /// </summary>
            /// <param name="threadCode"></param>
            /// <param name="threadInterval"></param>
            /// <param name="index"></param>
            public void StartThread(ThreadCode threadCode, int threadInterval, int index)
            {
                if (threadedCodeList[index].stopThread)
                {
                    threads[index] = new Thread(threadStarters[index]);
                    threadedCodeList[index].stopThread = false;
                    threads[index].Start();
                }
            }
    
            /// <summary>
            /// Method to tidy up unfinished threads.
            /// </summary>
            /// <param name="disposing"></param>
            protected override void Dispose(bool disposing)
            {
                for (int t = 0; t < threads.Count; t++)
                    KillThread(t);
    
                base.Dispose(disposing);
            }
        }

    The real change has been in the ThreadCodeObj, this now holds most of your threading information.

    /// <summary>
        /// This is the delegate to be used for passing the code to be called in the tread.
        /// </summary>
        /// <param name="gameTime">GameTime</param>
        public delegate void ThreadCode(GameTime gameTime);
    
        /// <summary>
        /// This class holds the required data for the code to be called in the thread.
        /// </summary>
        internal class ThreadCodeObj
        {
            public ThreadCode CodeToCall = null;
    
            /// <summary>
            /// Mutex to stop thread clashes.
            /// </summary>
            private static Mutex mutex = new Mutex();
            /// <summary>
            /// Used to make the thread wait.
            /// </summary>
            private ManualResetEvent threadStopEvent = new ManualResetEvent(false);
            /// <summary>
            /// Bool to control imediate stopping of thread loop.
            /// </summary>        
            public bool stopThread = false;
            /// <summary>
            /// Interval thread will wait befoer next cycle.
            /// </summary>        
            private int threadIntervals = 1;
    
    #if XBOX 
            int processorAffinity;
    #endif
    
            public GameTime gameTime;
    
    #if XBOX
            public ThreadCodeObj(ThreadCode code,int interval,int affinity)
            {
                CodeToCall = code;
                threadIntervals = interval;
                processorAffinity = affinity;
            }
    #else
            public ThreadCodeObj(ThreadCode code, int interval)
            {
                CodeToCall = code;
                threadIntervals = interval;
            }
    #endif
            public void Update(GameTime gameTime)
            {
                this.gameTime = gameTime;
            }
            public void Worker()
            {
    #if XBOX
                Thread.CurrentThread.SetProcessorAffinity(new int[] { processorAffinity });
    #endif
                do
                {
                    try
                    {
                        mutex.WaitOne();
    
                        if (gameTime != null)
                            CodeToCall(gameTime);
    
                    }
                    finally
                    {
                        mutex.ReleaseMutex();
                    }
                }
                while (!threadStopEvent.WaitOne(threadIntervals, false) && !stopThread);
            }
            public void KillThread(Thread thread)
            {
                if (!stopThread)
                {
                    mutex.WaitOne();
                    stopThread = true;
                    thread.Join(0);
                    mutex.ReleaseMutex();
                }
            }
        }

    At the top of the source file I have also included an affinity map so you know what processors you have access to, or should be using. XNA does not stop you from using a CPU/Core but you don't want to be running your threads on CPU cores that are already busy, it's why you are threading in the first place, because you have to.

    #if XBOX
        ///
        /// Processor affinity map.
        /// Index CPU CORE Comment
        /// -----------------------------------------------------------------------
        ///   0    1    1  Please avoid using. (used by 360)
        ///   1    1    2  Game runs here by default, so avoid this one too.
        ///   2    2    1  Please avoid using. (used by 360)
        ///   3    2    2  Part of Guide and Dashboard live here so usable in game.
        ///   4    3    1  Live market place downloads use this so usable in game.
        ///   5    3    2  Part of Guide and Dashboard live here so usable in game.
        /// -----------------------------------------------------------------------  
        ///
    #endif

    The sample solution is the same as the last one, but just uses this method now and you can download this here.

  • A visible collection of particles of water or ice suspended in the air, usually at an elevation above the earth's surface in XNA

    Well if I had just put, "Another Cloud Clip!" you would not have looked at this post. Anyway, her is some more of the same, but not quite the same, well, almost the same. In this clip I show multiple cloud layers each with differing tones of gray (I think it gives the cloud fields depth), also a splattering of around 100 clouds and then the cloud fields again but with some basic formation and dissipation effects in there.

    http://www.youtube.com/v/E5P3JBLLF4E <p><a href="http://www.youtube.com/v/E5P3JBLLF4E">http://www.youtube.com/v/E5P3JBLLF4E</a></p>

    Now as ever it looks a little laggy, I can't help this, but what I intend to do next time is to try and dig out my camera and film the cloud system off my TV to show how quick it is in my XBox 360, I would have done that this time around but it is dead... No RROD, it powered up, I get the normal green lights, one for power the other for my controller but I get no sound or video, which kind of detracts from game play a bit...

    So next time,  I hope to have a 360 clip up, quality will suck as it will be taken on my hand held camera, but with any luck you will see it run without lag.

  • Gamefest 2008

    Well managed to go! It was great, well the bits I saw as I didn't get there till around 11:00, but got to see most of Frank Savages talk on Threading in XNA which was interesting and threw up a few tips, also the talk on Live networking by Mitch Walker was very insightful.

    Now one talk that I did find changed my out look on an area of XNA was Frank Savages talk on the Content Pipeline. Now I knew you could do a fair bit with it, have played a little with it my self, but WOW! There is SO much in there to play with. An example he gave was when creating your levels in your modelling tool, use spheres for markers in the level for things like spawn points, event triggers etc.. label these spheres so you know what they are pertaining to, then you can write your own content override to get this data out of the model, use the centre of the sphere to mark the point, store it in an array of points and then store it in the tag of the Model (remember to remove this excess data prior to passing it on to the base class) It was so obvious yet I had not even thought about it, so much other stuff we could do in there too, I have been using it to store bounding box data as well as vertex data (not needed since 2.0 I don't think), but this could also include collision normals, further bounding data, breaking the model up into a number of spheres for greater collision accuracy, and that's just models, anything else that falls under the content project can be manipulated, this is still all pretty basic stuff, but I am sure you can imagine how much control we now have over our content.

    Well after all that exciting stuff, we had a break for lunch, I then went to the Community Games on XBox Live talk (after debating with myself if I should stay for the XBLA talk) and it was quite informative, nothing there I didn't really know or heard about already, other than the, "If your games kills anyone who plays it, it's your fault!" bit, well they didn't put it quite like that, but you are liable for your game I guess, being the publisher and the developer all in one... As you can imagine a lot of people in the room seemed to be industry folk and so there questions were more around the realms of IPR infringement and how they will now stop there developers moon lighting to make money with XNA. Oh and someone asked about people writing XBox time bomb's, malicious code that would get through the peer review as it only goes off at Christmas or Halloween, what I find odd about that is the limited access you have to both the network and the hardware on the 360, guess you could fill a hard drive or maybe flash up naughty pictures for a day, but I guess that's going to get you and your 360's mac address banned  for life....but then I guess there are some odd folk out there...you know who I mean....yes you there...no, not you, you...

    So then came the party after....I say party, but was just a gathering of the attendees for free booze and food, oh a blast on Geometry Wars and other titles around the room to, no Schitzoid which I found odd, would have thought they would have had a copy of that running at an XNA conference. Met some guys from Beatnick Games Ltd (Hi Robin) who are a start up writing a game in XNA, think they want us to play test the beta, so guess we are waiting for them to give us access now so we can rip it apart...

    Shame I missed the keynote and the What's new in XNA 3.0 talk, would have been good to catch those.

    All in all good stuff, I think I may now have an addiction to games dev conferences....ah, my personality flaws shining out again, first XNA now this....

    Thanks to Leaf and Robin for sorting stuff out and making sure I did not get lost on the tube ride back to St Pancs...