The 360 is Following a Strong Roadmap

Posted 04/25/2007 @ 06:30:34 AM by Joseph Molnar
Filed under: Xbox 360

Last month I started a four part series of posts evaluating the state of the current consoles. The previous article looked at the Nintendo Wii. Today I'm looking at the Microsoft Xbox 360.

Clearly There was a Plan

It is extremely clear that when Microsoft was planning the life of the 360 they thought long and hard about the ways to provide a complete package to the consumer and how to deliver that package over the lifetime of the console; Microsoft designed a roadmap to secure a solid footing in the consumers' living room.

To illustrate this, the following series of non-game timelines should give you a concise but thorough look at what Microsoft managed to deliver and what is known up through Holiday 2007. The timelines outline the variety and consistent release of accessories, the importance and strength of media, and the desire to open the platform up through instant messaging, homebrew development and cross-platform game play.

(Note: These mostly reflect North American dates.)

The Media Timeline
The Accessory Timeline
  • Nov. 2005 (launch): Wired/Wireless Controllers, Battery Packs, Wireless Networking Adapter, Play & Charge Kit
  • May 2006: Quick Charge Kit
  • Sep. 2006: Live Vision Camera
  • Nov. 2006: Wireless Racing Wheel, Wireless Headset, HD DVD Player (in media timeline)
  • Feb. 2007: Xbox 360 Wireless Receiver for Windows
  • Apr. 2007: Black Accessories, 120 GB hard drive
  • Summer 2007: Controller Keyboard (clearly planned for)
Other Timeline Notes
  • May 2006 - Live Anywhere announced
  • Dec. 2006 - XNA Game Studio Express Release (make games for 360 and Windows)
  • Apr. 2007 - XNA Game Studio Express Update
  • May 2007 - Dashboard update brings Windows Live Messenger Integration, Shadowrun is first 360/Windows LIVE Anywhere title

What about Games?

There are two stats that impress me. First stat, while the 360 had a year head start it is still very impressive to see that at the time of this writing Metacritic is reporting that the 360 has at least as many games rated above 80 as the PS3 and the Wii has games at any rating (the Wii's numbers don't include Virtual Console titles). The 360 has 49 titles rated 80 or above (with 176 titles total), PS3 has 40 total, and Wii has 49 total. Bottom-line: The 360 has a strong selection of games.

Second stat, with the latest March numbers for North America Microsoft is reporting that 5.7 games have been sold per console each month for the past 8 months. Even if the Wii manages to grab a larger portion of the market (and if the current trajectory holds, it will), the 360 is the console selling the most games which means publishers will keep putting the 360 on the target list.

In general Microsoft has done a good job managing the games on the platform:

  • Publisher of new IP by external developers such as Gear of War, Crackdown, Blue Dragon, Mass Effect, Lost Odyssey, Too Human
  • Developer of titles such as Kameo, Forza Motorsport 2, Halo 3, Fable 2, Banjo-Kazooie 3
  • Leveled the playing field by having older franchises on the 360 such as Devil May Cry 4, GTA IV, Virtua Fighter 5, Beautiful Katamari.
  • Gotten third party console exclusives such as Call of Duty 2, Dead Rising, Lost Planet, Bioshock, Overlord.

Amazingly, this doesn't including Arcade titles. There is no doubt that Live Arcade was growing slowly after the initial 14 launch titles. Now there are over 50 varying titles and Microsoft is periodically delivering multiple titles a week while expanding the range of titles. Live Arcade is proving to be the console download service to beat.

Not According to Plan

There is no doubt that Microsoft has done a great job putting together a strong plan and they are seeing the 360 become a strong contender for this generation's winning console. However, not everything has been going as smoothly as they had hoped.

Pricing

Microsoft would definitely sell more consoles if the 360 was cheaper, but quality issues, push for profitability and Sony's discontinuation of the 20 GB PS3 is keeping Microsoft from taking the plunge. But Microsoft should be cautious in allowing Sony to make the first price cut.

Console prices aside, the general sentiment is that the cost of items like the 120GB HD, Oblivion Horse Armour, Lumines Live and Guitar Hero 2 song packs are prohibitive.

Of course being the console micro-transaction pioneer means Microsoft will take a beating while they learn what does and doesn't work (while competitors get the lesson for free). Even though Microsoft doesn't set the price of downloads a large portion of the gaming population will still blame Microsoft and no explanation will help. While Microsoft knows what is or isn't purchased, what is harder to determine is if potential new customers are being scared away.

If blog and board comments are any measure, pricing has been loosing goodwill for Microsoft. Of course it doesn't help when developers describe their differences in opinion on how to get content to the consumer.

Quality

While manufacturing failures will always happen, it is clear that something was definitely wrong with Xbox 360s manufactured as late as April 2006 (perhaps later). Microsoft knows this and has been attempting to address the situation while changing their warranty to support customers.

Unfortunately the quality issues are broader. Key games required crucial patches right out of the gate. Others had patches for the patches. While developers need to do adequate testing so they don't hinder game sales, their failure impacts the 360's reputation as a whole.

Microsoft does have a certification process that involves some testing. The question is, does it makes sense for Microsoft to do more to ensure a higher minimum level of quality? If so, who eats the cost and will release dates be significantly impacted?

The 360 has unfortunately gained the reputation of being the least reliable console. Coupled with pricing issues and you have the two key forces working against the 360's sales and goodwill.

Japan

Poor reception of the original Xbox, a 360 weak launch catalog, and hardware issues has seriously put the 360 at a disadvantage in Japan. Highly rated titles like Blue Dragon aren't providing prolonged sales and support from firms ranging from Capcom to budget publisher D3 Publisher isn't having much of an impact. Microsoft was hoping for 1 million units sold within 8 months but instead is at less than 400,000 in nearly 18 months.

While Microsoft doesn't need to win Japan, growing Japanese developer support is crucial for both Japan and world-wide sales; it is going to take more than Devil May Cry, Trusty Bell and Lost Odyssey. A price cut, flagship titles like Final Fantasy and Metal Gear Solid, a Japanese Video Marketplace and better advertising would help Microsoft's Japanese efforts significantly.

Creativity

From a creative standpoint Microsoft is, oddly, the console traditionalist. Nintendo wowed the gaming world with a new controller, and Sony has been investing in more avant-garde titles like LittleBigPlanet and flOw. This isn't entirely surprising. After all, Nintendo and Sony are both entertainment companies first where Microsoft is a software technology company first.

Yes the amount of original IP on the 360 is strong but the number of uniquely creative games is rather small.

But Microsoft studios Rare and Lionhead Studios, as examples, could be strong creative champions. Microsoft needs to let them explore games as a medium for creativity, art and storytelling. Let the studios garner acclaim even if the games aren't mega-hits; it will help Microsoft gain much less fickle supporters.

It is a shame that Microsoft isn't still employing the ex-Lionhead folks behind LittleBigPlanet.

 

Articles in the series:

Developer Diary: XNA Threading - Locks

Posted 04/17/2007 @ 10:30:01 PM by Joseph Molnar
Filed under: Developer Diary , Programming , XNA

In my previous Developer Diary I discussed the problems that can arise from threaded development particularly as outlined in the example. In this post I discuss locks, what they are and how they can fix the problems with the example.

What are Locks?

The lock is one of the primary synchronization primitives in .NET. Locks are largely used to control access to a particular part of code (commonly called a critical section).

When a thread owns a lock no other thread is able to use the lock and execute the code protected by the lock. This results in the other threads waiting for the first thread to release its ownership. Once the first thread releases ownership one of the waiting threads will then own the lock and continue executing.

The basic way to create and use a lock is as follows:

class SomeClass {
    private object syncLock = new object( ); // create the lock in the class

   public void SomeMethod( ) {
        lock( syncLock ) { // request ownership of the lock
            // protected code here
        }                  // release ownership of lock
    }
}

The variable syncLock can actually be of any reference type. This means primitive and value types (such as int, float, Vector3, etc) cannot be used.

Updating Our Example

So how do locks help given our example? First let's update the code from the example in the previous Developer Diary (Note: the fully updated source is at the end of this post).

First we need our class-level shared lock.

        private object enemyDataLock = new object( );

After that we need to update the code in EnemyAIThreadMethod to lock just the area that updates the data that is used in the Draw method. You want to lock as little as possible otherwise you may slow down other threads who are also trying to use the lock. For example, if the Thread.Sleep( 10 ); statement was in the lock block then we would make it more difficult for the Draw method to maintain a high frame rate.

        private void EnemyAIThreadMethod( ) {
            while( !isStopping ) {
                // lock to make sure data is updated
                // correctly and Draw doesn't get 
                // partial results
                lock( enemyDataLock ) {
                    // NOTE: this is just filler code.
                    // Real code would analyze user location and if multiple
                    // enemies perform flocking, or other behaviours.
                    enemyRotation -= 0.01f;
                    enemyVelocity += new Vector3( 1, 0, 1 );
                    enemyPosition += enemyVelocity;
                }
                // sleeping will probably be in order, but outside 
                // of the lock so we have Draw a chance
                Thread.Sleep( 10 );
            }
        }

Finally we update the Draw method. At first glance you may consider simply putting a lock around the entire foreach block to ensure that we use the same values for enemyRotation and enemyPosition for each mesh in the model. The Draw method would look like this:

        public override void Draw( GameTime gameTime ) {
            // ... draw other aspects of the scene....

            // Copy any parent transforms.
            Matrix[ ] transforms = new Matrix[ enemyModel.Bones.Count ];
            enemyModel.CopyAbsoluteBoneTransformsTo( transforms );

            // make sure we have consistent data
            lock( enemyDataLock ) {
                // draw the enemy model
                foreach( ModelMesh mesh in enemyModel.Meshes ) {
                    foreach( BasicEffect effect in mesh.Effects ) {
                        effect.EnableDefaultLighting( );
                        // ... but ensure we draw it at the right location 
                        effect.World = transforms[ mesh.ParentBone.Index ]
                            * Matrix.CreateRotationY( enemyRotation )
                            * Matrix.CreateTranslation( enemyPosition );
                        effect.View = Matrix.CreateLookAt(
                            cameraPosition,
                            Vector3.Zero,
                            Vector3.Up );
                        effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                            MathHelper.ToRadians( 45.0f ),
                            aspectRatio,
                            1.0f,
                            10000.0f );
                    }
                    mesh.Draw( );
                }
            }

            // ... draw other aspects of the scene....
            
            base.Draw( gameTime );
        }

That is a lot of code to lock. Remember you want to lock as little as possible. A better approach is to create local copies of the data and only lock the point where the data is copied. Our new Draw method looks as follows:

        public override void Draw( GameTime gameTime ) {
            // ... draw other aspects of the scene....

            // Copy any parent transforms.
            Matrix[ ] transforms = new Matrix[ enemyModel.Bones.Count ];
            enemyModel.CopyAbsoluteBoneTransformsTo( transforms );

            // local copies of the class data
            Vector3 enemyPositionCopy;
            float enemyRotationCopy;

            // make sure we have consistent data
            lock( enemyDataLock ) { 
                enemyPositionCopy = enemyPosition;
                enemyRotationCopy = enemyRotation;
            }
            
            // draw the enemy model
            foreach( ModelMesh mesh in enemyModel.Meshes ) {
                foreach( BasicEffect effect in mesh.Effects ) {
                    effect.EnableDefaultLighting( );
                    // ... but ensure we draw it at the right location 
                    effect.World = transforms[ mesh.ParentBone.Index ]
                        * Matrix.CreateRotationY( enemyRotationCopy )
                        * Matrix.CreateTranslation( enemyPositionCopy );
                    effect.View = Matrix.CreateLookAt(
                        cameraPosition,
                        Vector3.Zero,
                        Vector3.Up );
                    effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                        MathHelper.ToRadians( 45.0f ),
                        aspectRatio,
                        1.0f,
                        10000.0f );
                }
                mesh.Draw( );
            }

            // ... draw other aspects of the scene....

            base.Draw( gameTime );
        }
    }

This seriously cuts down on the amount of code locked allowing both threads to run more quickly. As a note, the Vector3 type is a value type (struct), instead of a reference type (class). This means the line enemyPositionCopy = enemyPosition; isn't simply assigning a reference, but making a field by field copy of the Vector3 members. This is an important distinction. If we had reference types then other code would need to be considered. I'll cover that in my next article.

Why Does It Work?

I mentioned in the previous article that our example needed updating to cover two problems.

The first problem was uncontrolled access to the data. This is fixed by the lock statements. The use of a lock prevents the Draw thread from using the enemy data while the data is being updated in the EnemyAIMethod thread and conversely the EnemyAIMethod can't update the values while the Draw method is copying them.

The second problem was getting around .NET's memory and synchronization model. The reason this isn't an issue is due to how locks work. I'll attempt a simple explanation since it is much more complicated than I'm describing. Locks explicitly indicate to .NET that there is threaded code so that certain optimizations will not occur. Some optimizations, such as the caching of data in processor and system caches, aren't an issue because a lock ensures that modified data makes it in and out of main memory.

As you may have guessed using locks incurs a performance penalty. However, it can be a small price to pay for correctness. More advanced options are available, but they are harder to code for. I'll outline one such approach in my next post.

In Summary

Locks are an important part of threading in .NET but, as with all things, they have their advantages and disadvantages.

Advantages of locks:

Disadvantages of locks:

  • Is fairly expensive to execute
  • Easier to create deadlock situations
What is a Deadlock?

It is best to discuss deadlocks when you have an example. Fortunately the XNA example code we have been using isn't subject to deadlocks, but the following piece of code is:

private object syncLockA = new object( );
private object syncLockB = new object( );

// thread one is running here
private void ThreadMethodOne( ) {
    while( !isStopping ) {
        lock( syncLockA ) {
            // do some work
            lock( syncLockB ) {
                // do some more work
            }
        }
    }
}

// thread two is running here
private void ThreadMethodTwo( ) {
    while( !isStopping ) {
        lock( syncLockB ) {
            // do some work
            lock( syncLockA ) {
                // do some more work
            }
        }
    }
}

The potential for deadlocks occur when two or more threads have two or more locks they are sharing. In the above code a deadlock can occur if we have a thread executing in ThreadMethodOne that is currently trying to acquire syncLockB after having acquired syncLockA, and we have another thread executing in ThreadMethodTwo trying to acquire syncLockA after having acquired syncLockB. These threads are now stuck forever since they are waiting for the other thread to release the additional lock needed.

That's a deadlock. Once you have multiple threads and multiple locks you need to be extra careful with your code.

Follow-on Posts

I hope this post gave you a good overview of how to use locks in .NET. In my next post I'll talk about another mechanism that can be used to handle our example, as well as some pointers for additional .NET threading details and recommendations.

 

Complete Updated Example Source Code

using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

namespace SampleThreading {
    public class ThreadedEnemyComponent : DrawableGameComponent {
        // general members
        private ContentManager content;
        Vector3 cameraPosition = new Vector3( 0.0f, 50.0f, -5000.0f );
        float aspectRatio = 640.0f / 480.0f;

        // thread members
        private Thread enemyAIThread = null;
        private volatile bool isStopping = false;
        private object enemyDataLock = new object( );

        // enemy specific data (model, velocity, position rotation, etc).
        private Model enemyModel;
        private Vector3 enemyVelocity = Vector3.Zero;
        private Vector3 enemyPosition = Vector3.Zero;
        private float enemyRotation = 0.0f;

        /// <summary>
        /// Basic constructor taking the game the component is to be a part of.
        /// </summary>
        /// <param name="theGame">The game the component will belong to.</param>
        public ThreadedEnemyComponent( Game theGame )
            : base( theGame ) {
            content = new ContentManager( this.Game.Services );
        }

        /// <summary>
        /// Override Initialize to start our separate AI thread.
        /// </summary>
        public override void Initialize( ) {
            base.Initialize( );

            enemyAIThread = new Thread( EnemyAIThreadMethod );
            enemyAIThread.Start( );
        }

        /// <summary>
        /// Override dispose to ensure our thread is shutdown.
        /// </summary>
        /// <param name="disposing">
        /// Set to true to release manage and unmanaged resources.
        /// Set to false to release unmanaged resources.
        /// </param>
        protected override void Dispose( bool disposing ) {
            try {
                isStopping = true;
                if( disposing ) {
                    // let's shutdown our thread if it hasn't
                    // shutdown already
                    if( enemyAIThread != null ) {
                        enemyAIThread.Join( ); // wait for the to shutdown
                        enemyAIThread = null;
                    }
                }
            } finally {
                base.Dispose( disposing );
            }
        }

        protected override void LoadGraphicsContent( bool loadAllContent ) {
            if( loadAllContent ) {
                // load our enemy model
                enemyModel = this.content.Load<Model>( @"Content\Models\
enemyModel" );
            }
        }

        protected override void UnloadGraphicsContent( bool unloadAllContent ) {
            if( unloadAllContent == true ) {
                content.Unload( );
            }
        }

        /// <summary>
        /// The thread that runs the enemy AI
        /// </summary>
        private void EnemyAIThreadMethod( ) {
            while( !isStopping ) {
                // lock to make sure data is updated
                // correctly and Draw doesn't get 
                // partial results
                lock( enemyDataLock ) {
                    // NOTE: this is just filler code.
                    // Real code would analyze user location and if multiple
                    // enemies perform flocking, or other behaviours.
                    enemyRotation -= 0.01f;
                    enemyVelocity += new Vector3( 1, 0, 1 );
                    enemyPosition += enemyVelocity;
                }
                // sleeping will probably be in order, but outside 
                // of the lock so we have Draw a chance
                Thread.Sleep( 10 );
            }
        }

        /// <summary>
        /// The thread that does the drawing from frames
        /// </summary>
        public override void Draw( GameTime gameTime ) {
            // ... draw other aspects of the scene....

            // Copy any parent transforms.
            Matrix[ ] transforms = new Matrix[ enemyModel.Bones.Count ];
            enemyModel.CopyAbsoluteBoneTransformsTo( transforms );

            // local copies of the class data
            Vector3 enemyPositionCopy;
            float enemyRotationCopy;

            // make sure we have consistent data
            lock( enemyDataLock ) {
                enemyPositionCopy = enemyPosition;
                enemyRotationCopy = enemyRotation;
            }
            
            // draw the enemy model
            foreach( ModelMesh mesh in enemyModel.Meshes ) {
                foreach( BasicEffect effect in mesh.Effects ) {
                    effect.EnableDefaultLighting( );
                    // ... but ensure we draw it at the right location 
                    effect.World = transforms[ mesh.ParentBone.Index ]
                        * Matrix.CreateRotationY( enemyRotationCopy )
                        * Matrix.CreateTranslation( enemyPositionCopy );
                    effect.View = Matrix.CreateLookAt(
                        cameraPosition,
                        Vector3.Zero,
                        Vector3.Up );
                    effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                        MathHelper.ToRadians( 45.0f ),
                        aspectRatio,
                        1.0f,
                        10000.0f );
                }
                mesh.Draw( );
            }

            // ... draw other aspects of the scene....

            base.Draw( gameTime );
        }
    }
}

Developer Diary: XNA Threading - The Problems

Posted 04/15/2007 @ 10:30:36 AM by Joseph Molnar
Filed under: Developer Diary , Programming , XNA

As an extension to my previous Developer Diary I thought it made sense to give some more background information on threading in .NET and how it may apply to XNA development. In part this is intended to provide some insight into the difficulties of threaded development. This will be a three post backgrounder.

This first post will introduce the example and discuss the problems with it. This example will be the point of reference for all three posts.

The Example

The premise of the example is that you want to put your enemy AI code in a separate thread so that Update and Draw will have as much time as possible to execute. The below example code is derived from a Microsoft example on how to draw models. I have modified the example to do AI calculations in a separate thread (there is no real AI code, just filler).

using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

namespace SampleThreading {
    public class ThreadedEnemyComponent : DrawableGameComponent {
        // general members
        private ContentManager content;
        Vector3 cameraPosition = new Vector3( 0.0f, 50.0f, -5000.0f );
        float aspectRatio = 640.0f / 480.0f;

        // thread members
        private Thread enemyAIThread = null;
        private bool isStopping = false;

        // enemy specific data (model, velocity, position rotation, etc).
        private Model enemyModel;
        private Vector3 enemyVelocity = Vector3.Zero;
        Vector3 enemyPosition = Vector3.Zero;
        float enemyRotation = 0.0f;

        /// <summary>
        /// Basic constructor taking the game the component is to be a part of.
        /// </summary>
        /// <param name="theGame">The game the component will belong to.</param>
        public ThreadedEnemyComponent( Game theGame )
            : base( theGame ) {
            content = new ContentManager( this.Game.Services );
        }

        /// <summary>
        /// Override Initialize to start our separate AI thread.
        /// </summary>
        public override void Initialize( ) {
            base.Initialize( );

            enemyAIThread = new Thread( EnemyAIThreadMethod );
            enemyAIThread.Start( );
        }

        /// <summary>
        /// Override dispose to ensure our thread is shutdown.
        /// </summary>
        /// <param name="disposing">
        /// Set to true to release manage and unmanaged resources.
        /// Set to false to release unmanaged resources.
        /// </param>
        protected override void Dispose( bool disposing ) {
            try {
                isStopping = true;
                if( disposing ) {
                    // let's shutdown our thread if it hasn't
                    // shutdown already
                    if( enemyAIThread != null ) {
                        enemyAIThread.Join( ); // wait for the to shutdown
                        enemyAIThread = null;
                    }
                }
            } finally {
                base.Dispose( disposing );
            }
        }

        protected override void LoadGraphicsContent( bool loadAllContent ) {
            if( loadAllContent ) {
                // load our enemy model
                enemyModel = this.content.Load<Model>( @"Content\Models\
enemyModel" );
            }
        }

        protected override void UnloadGraphicsContent( bool unloadAllContent ) {
            if( unloadAllContent == true ) {
                content.Unload( );
            }
        }

        /// <summary>
        /// The thread that runs the enemy AI
        /// </summary>
        private void EnemyAIThreadMethod( ) {
            while( !isStopping ) {
                // NOTE: this is just filler code.
                // Real code would analyze user location and if multiple
                // enemies perform flocking, or other behaviours.
                enemyRotation -= 0.01f;
                enemyVelocity += new Vector3( 1, 0, 1 );
                enemyPosition += enemyVelocity;
                // sleeping will probably be in order.
                Thread.Sleep( 10 );
            }
        }

        /// <summary>
        /// The thread that does the drawing from frames
        /// </summary>
        public override void Draw( GameTime gameTime ) {
            // ... draw other aspects of the scene....

            // Copy any parent transforms.
            Matrix[ ] transforms = new Matrix[ enemyModel.Bones.Count ];
            enemyModel.CopyAbsoluteBoneTransformsTo( transforms );

            // draw the enemy model
            foreach( ModelMesh mesh in enemyModel.Meshes ) {
                foreach( BasicEffect effect in mesh.Effects ) {
                    effect.EnableDefaultLighting( );
                    // ... but ensure we draw it at the right location 
                    effect.World = transforms[ mesh.ParentBone.Index ] 
                        * Matrix.CreateRotationY( enemyRotation )
                        * Matrix.CreateTranslation( enemyPosition );
                    effect.View = Matrix.CreateLookAt( 
                        cameraPosition, 
                        Vector3.Zero, 
                        Vector3.Up );
                    effect.Projection = Matrix.CreatePerspectiveFieldOfView( 
                        MathHelper.ToRadians( 45.0f ),
                        aspectRatio, 
                        1.0f, 
                        10000.0f );
                }
                mesh.Draw( );
            }

            // ... draw other aspects of the scene....
            
            base.Draw( gameTime );
        }
    }
}

The Problems with the Sample

The thread method EnemyAIThreadMethod does a slew of calculations while the Draw method uses the results of the calculations to display the model. If you didn't know otherwise the above code looks perfectly fine.

In fact when you run it, more often than not it will behave as you would expect. However, it won't always be right and I don't mean per running of the application. On a frame-by-frame basis you may see things behave in an unexpected fashion, such as an odd warping of the model. There are two main reasons for this.

Uncontrolled Access to Data

Since there are two separate threads using the data, in this case one reading (Draw) and one writing (EnemyAIThreadMethod), it is possible that the Draw thread will use values for enemyPosition, enemyRotation, etc, while they are changing. In other words, in a single frame for a single model some of the meshes in the model could use old values while other meshes for the same model could use newer values. This will result in some rather interesting looking (as in bad) models.

.NET Memory and Synchronization Model

Another reason for odd behaviour is related to how .NET works. .NET is essentially defined such that if nothing in a method or in a member declaration indicates there are multiple threads then .NET assumes it is single threaded code. The simple declaring and starting of a thread, as done in the above sample, isn't sufficient. Your code needs to indicate its awareness of threads.

Why? .NET attempts to optimize the code through a variety of techniques. A common optimization is to cache data in processor or system caches. For example, enemyRotation could be cached separately in the EnemyAIThreadMethod thread and the Draw thread. This means that when you change enemyRotation in the EnemyAIThreadMethod thread the Draw thread may not see the change until something triggers the caches to flush back to main memory. The reason for caching is speed; accessing a cache is much faster than accessing main memory.

Follow-on Posts

This post was just meant to introduce the typical problems encountered with threading. The follow-on posts, which I'll post over the next couple days, will cover ways to correct the problems outlined here.

News: Sony Dropping 20 GB PlayStation 3

Posted 04/11/2007 @ 10:30:15 PM by Joseph Molnar
Filed under: News Picks , PlayStation 3

While over the past few weeks there has been plenty of evidence that this change was coming, the news has still provoked a lot of discussion. While on the surface this move may simply appear to give Microsoft more pricing breathing room particularly with the introduction of the Xbox 306 Elite, this is a good move for Sony and future PS3 purchasers.

That may sound like an odd statement since things are forcibly more expensive to today's consumer, but keep the following things in mind.

First, this time of year generally has lower sales, the 20 GB model is not even offered in all territories and Sony's bigger games don't come out the door until the second half of the year. Therefore the impact on sales is relatively minor.

Second, Sony has been aggressively cost cutting by doing things like removing chip-level PS2 compatibility, shrinking the internal chips, etc. With the 20 GB PS3 reportedly loosing more more money than the 60 GB version, and the additional cost to manage two separate lines (manufacturing, packaging, shipping, etc), removing the 20 GB PS3 will save Sony money and potentially allow for a price reduction sooner than had both lines been fully operational.

Third, people are buying substantially more 60 GB models than 20 GB. Yes people are arguing that this is simply because Sony is purposefully not creating as many, but this is even more reason to drop the 20 GB model. Sony had to either fully commit to the success of the 20 GB PS3 or drop it entirely. Half-measures are just more costly, distracting and ultimately more confusing for consumers particularly when the 20 GB version is hard to find.

So while on the surface this may seem like a minor set back ultimately this feels like the right decision as we march toward the later half of the year.

Developer Diary: XNA Game Loop and Threading

Posted 04/10/2007 @ 06:30:35 AM by Joseph Molnar
Filed under: Developer Diary , Programming , Xbox 360 , XNA

In my previous Developer Diary I talk about the tools and tutorials I used to get some background on XNA. After playing with the tutorials I decided to start digging into the mechanics of what makes an XNA game. The big defining elements are the two main Game methods, Draw and Update. In the process of looking at these method I thought I would give you some insight into .NET threading.

Note: Most of my sample code will be implemented as DrawableGameComponents instead of actual Games. I found this the most convenient way to bring code into an existing project.

Main Game Loops and Threading

Draw, which is intended for drawing frames, and Update, which is intended to run game logic, are called automatically by the XNA framework. When I saw these methods I had assumed they were called via different threads. After all, game developers typically try to achieve 60 frames per second (fps) and therefore try to remove any potential contention.

I was wrong. In .NET you typically get the current thread id using Thread.CurrentThread.GetManagedThreadId. For example, in the following code I retrieve the current thread id in both the Draw and Update methods.

/// <summary>
/// Sample class for checking thread id.
/// </summary>
public class CheckThreadIdClass : DrawableGameComponent {
    /// <summary>
    /// Constructor required for the game component.
    /// </summary>
    /// <param name="theGame">The Game this component will be used by.</param>
    public CheckThreadIdClass( Game theGame )
        : base( theGame ) {
    }

    /// <summary>
    /// The method used to draw frames.
    /// </summary>
    /// <param name="gameTime">GameTime since last call.</param>
    public override void Draw( GameTime gameTime ) {
        base.Draw( gameTime );
        // get the thread that is being called by this thread.
        int currentThreadId = Thread.CurrentThread.ManagedThreadId;
    }

    /// <summary>
    /// The method used to update game location.
    /// </summary>
    /// <param name="gameTime">GameTime since last call.</param>
    public override void Update( GameTime gameTime ) {
        base.Update( gameTime );
        // get the thread that is being called by this thread.
        int currentThreadId = Thread.CurrentThread.ManagedThreadId;
    }
}

Under Windows you will get the same thread id value in both Draw and Update (probably 1). Under the 360 both methods also report the same value, but it is a large negative value, -117440492 (not sure what this value means). From here I added some code to create a new thread to see what its thread id is.

/// <summary>
/// Sample class for creating a thread.
/// </summary>
public class CreateThreadClass : DrawableGameComponent {
    private Thread extraThread = null;
    private volatile bool isStopping = false;

    /// <summary>
    /// Constructor required for the game component.
    /// </summary>
    /// <param name="theGame">The Game this component will be used by.</param>
    public CreateThreadClass( Game theGame )
        : base( theGame ) {
    }

    public override void Initialize( ) {
        base.Initialize( );
        // create and start thread
        extraThread = new Thread( ThreadMethod );
        extraThread.Start( );
    }

    /// <summary>
    /// The method used to draw frames.
    /// </summary>
    /// <param name="gameTime">GameTime since last call.</param>
    public override void Draw( GameTime gameTime ) {
        base.Draw( gameTime );
        // get the thread that is being called by this thread.
        int currentThreadId = Thread.CurrentThread.ManagedThreadId;
    }

    /// <summary>
    /// The method used to update game location.
    /// </summary>
    /// <param name="gameTime">GameTime since last call.</param>
    public override void Update( GameTime gameTime ) {
        base.Update( gameTime );
        if( GamePad.GetState( PlayerIndex.One ).Buttons.Back == ButtonState.
Pressed ) {
            ShutDown( );
        } else {
            // get the thread that is being called by this thread.
            int currentThreadId = Thread.CurrentThread.ManagedThreadId;
        }
    }

    /// <summary>
    /// Method called when we are to shutdown the game.
    /// </summary>
    private void ShutDown( ) {
        isStopping = true;
        // wait for the thread to die
        if( this.extraThread != null ) {
            this.extraThread.Join( );
            this.extraThread = null;
        }
        // now exit
        this.Game.Exit( );
    }

    /// <summary>
    /// Sample thread method.
    /// </summary>
    private void ThreadMethod( ) {
        // get the thread that is being called by this thread.
        int currentThreadId = Thread.CurrentThread.ManagedThreadId;

        while( !isStopping ) {
        }
    }
}

On the Xbox 360 the new Thread's thread id returned a similar large negative value, -117440472. At this point I recalled that Microsoft added a special method, Thread.SetProcessorAffinity, for the 360 that allows you to associate a software thread to hardware thread (one of the six the 360 has). So I changed the ThreadMethod method to use a particular hardware thread.

Note: Thread.SetProcessorAffinity must be called within the thread that wishes to run on particular hardware threads.

/// <summary>
/// Sample thread method.
/// </summary>
private void ThreadMethod( ) {
    // get the thread that is being called by this thread.
    int currentThreadId = Thread.CurrentThread.ManagedThreadId;
#if XBOX360
    // set the processor threads to run on
    Thread.CurrentThread.SetProcessorAffinity( new int[ ] { 3 } );
    // re-get the thread id
    currentThreadId = Thread.CurrentThread.ManagedThreadId;
#endif
    while( !isStopping ) {
    }
}

The Xbox 360 returned the same negative value, -117440472. Needless to say, it is a safe assumption that Draw and Update are in the same thread.

The reason for this is simple. Having Draw and Update in the same thread is easier for novices. Writing threaded code when sharing data between threads, as would be the case if Draw and Update were in separate threads, is trickier than it may seem.

Some Threading Notes of Interest

The MSDN Library description for Thread.SetProcessorAffinity has a description of the 360's threading (see the remarks section). Of interest is how many threads are actually listed as either being reserved or partially used by the 360's system software and Dashboard. It seems there is more overhead than I recall from blogs and podcasts on this subject. Perhaps this breakdown is specific to XNA on the 360?

From a tool standpoint, there is one other item of interest. It appears that Visual C# Express does not support the thread debug window, which allows you to view, switch and freeze threads. While some may consider this a mild annoyance there are certain circumstances, like deadlock debugging, that will be difficult to debug without this feature; I would love for Microsoft to allow XNA to be installed into non-Express editions of Visual Studio 2005. Microsoft has stated this may come in a future update to the XNA tools, though most likely not in the update coming this month.

Follow-on Post

I'll continue my discussion on threading, delving into implementation options and difficulties over the next few post:

Recent Developer Diary Articles

Site News: April 09, 2007

Posted 04/09/2007 @ 06:30:21 AM by Joseph Molnar
Filed under: Site News

Lack of Posts

Sorry for the long delay. A few things have been to blame including a trip up to my office in Toronto. My Toronto trips, which I do numerous times through-out the year, generally leave me with very little free time. Next time I'll post a note before I leave.

Mind you, the delay was also due to a tool I decided to write.

Source Code to HTML Formatting Tool

I've been working on my XNA articles (at least one will be out this week) but wasn't happy with existing web or stand-alone tools that format source code for blogs/web-sites. So I spent a few nights putting a tool together; it's called Trellis.

Trellis is complete except for a nice icon which a friend of mine should be sending over tomorrow. The tool is much more customizable than most things I've seen out there and runs either as a standalone application or as a Windows Live Writer plug-in. The plug-in was extremely simple but important to me since I tend to write my articles using Live Writer. The code was also designed to be incorporated into other .NET applications including .NET based websites and blogging engines. I will include C# and HLSL (.fx files) formatters, but you can easily create your own formatters using a text file.

I'll post the tool up for download, along with more details, once I receive the icon.

Missed News

While I have been reading news I haven't had time to comment on what has been going on and there has actually been quite a lot including the PS3 Launch in Europe, the 360 Elite announcement and even yesterday's 360 May Dashboard update and keyboard announcement. Needless to say, I'll be back at it again as of now.

Curiousity #7: Video Marketplace Rental Period

Posted 04/09/2007 @ 06:30:00 AM by Joseph Molnar
Filed under: Media , The Weekly Curiousity , Xbox 360

The Xbox Live Video Marketplace is a convenient way to rent movies and my wife and I have noticed a nice increase in films that suit our tastes, including The Departed and Babel.

What we don't understand is the rental period. Once you start a movie you can watch it as often as you like over the next 24 hours. This time period is too short.

My wife and I generally start a film after we put our little one to bed. There have been a few times where we wanted to stop and continue the movie the next day. Since movies expire 24 hours after they start we would be unable watch the rest. Why not change it to something like 36 hours?

Yes some folks will abuse this but lending your Xbox 360 hard disk is a lot less convenient than lending someone a DVD rented from a brick and mortar store.