Content Pipeline assemblies

One of the more confusing things for newcomers to the XNA Framework Content Pipeline is figuring out how (and why) to split your code across multiple assemblies.

First off, let's examine the flow of data through the pipeline:


My choice of colors is not random. In fact, knowing why I colored some boxes blue but others red is probably the most important part of understanding the Content Pipeline!

  • The blue boxes run inside Visual Studio while building your content project
  • The red boxes run as part of the finished game

This core difference implies other important distinctions:

  • The blue parts only ever run on your development computer
  • The red parts are used by everyone who plays your game
  • Blue parts always run on Windows
  • Red parts may run on Windows, Xbox, or Zune
  • Blue parts use classes from the Microsoft.Xna.Framework.Content.Pipeline assembly
  • Red parts must work on computers that only have the framework redistributable installed, rather than the full Game Studio, so they must not reference the Content Pipeline assembly
  • When you release your game, you must include all the red parts, but there is no need to distribute the blue parts

You don't always have to implement all the boxes. For instance it is common to write a custom content processor, but use one of the built-in importers. It is also common for all three objects A, B, and C to be of the same type. Let's look at some examples...


Processing Existing Data Types

The simplest scenario is writing a custom content processor to modify an existing data type. A good example is the NormalMapProcessor from the Sprite Effects sample:

  • Uses the built-in TextureImporter
  • Object A is the built-in TextureContent class
  • The custom NormalMapProcessor is implemented by the SpriteEffectsPipeline assembly
  • Object B is again the built-in TextureContent class
  • Uses the built-in ContentTypeWriter for TextureContent data
  • Uses the built-in ContentTypeReader for Texture2D data
  • Object C is the built-in Texture2D class

Note how the sample includes two projects: SpriteEffectsWindows contains the main game code, while SpriteEffectsPipeline contains the custom processor code.

If you open the Xbox version of the solution, you will see that even though the SpriteEffectsXbox game project targets the Xbox 360 platform, SpriteEffectsPipeline still targets Windows (x86).


Creating New Data Types

A more interesting scenario occurs if we want to add a new data type. The Custom Model Class sample is a good example of this:

  • Uses the built-in ModelImporter
  • Object A is the built-in NodeContent class
  • Provides a CustomModelProcessor
  • Object B is the CustomModelContent class
  • Provides a CustomModelContentWriter
  • Provides a CustomModelReader
  • Object C is the CustomModel class

Again the solution contains two projects, and again the pipeline extension project is always built for Windows, even when the game is targeting Xbox. The important thing is which types are defined in which projects:

  • The CustomModelPipeline project defines the components that are blue in my diagram: CustomModelProcessor, CustomModelContent, and CustomModelContentWriter
  • The game project defines the red components: CustomModel and CustomModelReader


Sharing Data Types Between Build And Runtime

It often turns out that Object B and Object C from my diagram are actually the same type.

Where should we define such a shared type?

We can't put it in the pipeline extension project, because we don't ship that project along with the final game. We also can't put it in the game project, because that would create a circular dependency: we need to use Object B to build content for the game, but if Object B is defined as part of that game, it wouldn't exist before the game was built. We can't use the game to build itself!

Types that are used both during the content build and inside the game itself are no longer purely blue or red. They introduce a third category: let's call it purple. We need a new assembly to contain such things.

You can find an example of this in the Skinned Model sample:

  • Uses the built-in ModelImporter
  • Object A is the built-in NodeContent class
  • Provides a SkinnedModelProcessor
  • Object B has its Tag set to the custom SkinningData class
  • Provides a SkinningDataWriter
  • Provides a SkinningDataReader
  • Object C again uses the custom SkinningData class

The interesting thing here is which assemblies define each type:

  • The SkinnedModelPipeline project defines the purely blue components: SkinnedModelProcessor and SkinningDataWriter
  • The game project (SkinningSampleWindows) defines the purely red components, which in this case is just the Game class, although the ContentTypeReader could also be implemented here
  • The third project (SkinnedModelWindows) defines the shared, purple types: SkinningData and SkinningDataReader

Note how both SkinningModelPipeline and SkinningSampleWindows have references to the shared SkinnedModelWindows project. This allows them both to access the same SkinningData type, so it can be used for both Object B and Object C as data flows through the pipeline.

But what about Xbox? If our game is for Xbox, we can no longer share a single project between the pipeline extension project (which runs on Windows) and the game (which runs on Xbox).

The answer can be found in SkinningSampleXbox.sln. This contains not three, but four separate projects. But two of these are actually the same thing, building the same source code for different platforms. Thus we have both SkinnedModelWindows, which defines the shared type for the Windows platform so it can be referenced by the SkinnedModelPipeline, and also SkinnedModelXbox, which includes the same .cs files and defines the same shared type, but this time for the Xbox platform so it can be referenced by the SkinningSampleXbox game project.

In other words:

  • Game project: builds for Xbox
  • Pipeline extension project: always builds for Windows
  • Shared data types: there are two versions of this project, so it is built twice, once for Windows and then again for Xbox

Comments (14)

Cancel reply

  1. Metalov says:

    Hi Shawn,

    Just out of curiosity why did you (read XNA team,i realize I am just asking for an unofficial answer) choose fbx and not collada format?Is it because of commercial reasons or partnership?Or maybe because collada isn’t really mature yet?

  2. ShawnHargreaves says:

    We didn’t exactly "not" choose Collada format: we understood that there are many interesting formats out there (imagine how cool it would be if you could import directly from .max or .mb files, for instance!) so it was important to design a system that could support many different formats. This is the main reason for the separate importer stage, as this lets people develop importers for many different file formats, but have them all use the same standard object model and processors.

    When it came to which importers we wrote ourselves to ship built in to the product, time was very limited, so we just had to pick whichever we thought would give us the biggest bang for our limited buck.

  3. nuvem says:

    Shawn, is there any other solution to the shared data type problem other than creating duplicate projects? What conflict prevents one from creating a single Xbox project that is also imported into the Windows pipeline extension project?

  4. ShawnHargreaves says:

    > What conflict prevents one from creating a single Xbox project that is also imported into the Windows pipeline extension project?

    You cannot load and use an Xbox project on Windows (or vice versa). Projects are specific to a platform, and can only be used on the platform they were built for.

    Fortunately, creating duplicate projects is trivially easy once you realize that you have to do this, thanks to the cross platform project management features in Game Studio.

  5. Ari Velazquez says:

    Just wanted to say that this diagram and your bullet points have made this entire framework make sense INSTANTLY. You should convince the team to include that image in every new XNA project that Visual Studio builds so everyone can see it right away.

  6. Chris Egner says:

    A little more than a year later, and this is still very useful. Thanks for the info Shawn.

  7. Justin Tubbs says:

    I’ve downloaded the sample 3.1 and have added it into my Windows Game Solution.

    The default tank.fbx file shows the custom content processor, but if I import other .fbx files into this project, go to Properties, and look at the options in the dropdown for "Content Processor", I don’t see "CustomModelProcessor".

    Is there some other reason why I cannot see that custom content processor in this solution?  I’m completely confused as to why "tank.fbx" shows it, but other imported model files only show the default content processors.

  8. ShawnHargreaves says:

    Hey Justin,

    This means your content project is not correctly referencing the content pipeline extension DLL.

    Most likely you either forgot to add a reference from your ContentReferences folder (not the main game references!) to the pipeline extension project.

    Or perhaps the pipeline extension project is using a different version of Game Studio to your game?

  9. James Brauman says:

    Hey Shawn,

    Just wanted to tell you what a fantastic resource this page is! I’ve been struggling to understand the Content Pipeline but this page has helped a lot.


  10. Thank you for this great article! It opened my eyes for the solution for my problem!

    I thought Object B and C have to be the same…

  11. Michael Blackburn says:

    I’d like to build an XNA app that will allow a user to create a sprite using themselves, but this seems to say I can’t do it! Especially with Natal, I would think that personalization would be a high priority!

  12. Marveh says:

    Does this still work with XNA 4 especially as there is now a separate 'Content Reference'. I for one cannot get it to work, I have a shared game library with 'object C' in and it's ContentReader, and this is referenced by my Content Pipeline project and the Game project, compilation of the Game project fails with "There was an error while deserializing intermediate XML. Cannot find type "GameShared.Level", I tried adding the reference for GameShared to the Content project but things just got a whole lot worse and gave me this error instead.. "Error loading pipeline assembly "BioLabShared, Version=, Culture=neutral, PublicKeyToken=null"

    Any thoughts?

  13. ShawnHargreaves says:

    Marveh: none of this stuff changed in Game Studio 4. I would recommend the forums to get help with your problem – blog comments aren't really the best place for doing tech support!

  14. Marveh says:

    Hi Shawn,

    Thanks, incase anyone else has this issue, i did find this VERY useful snippet on the forums

    "When adding references to a content project, you must always  reference a Windows project. If you need to reference a custom class that you defined in a Windows Phone Game Library project, then right-click that project and select "Create Copy of Project for Windows". This will create a clone of the first project (that shares the same source files), but which builds the assembly for Windows. Add the reference from the content project to the Windows Copy of your game library. The content pipeline needs this because the content pipeline always executes on your local Windows machine. When you mix-and-match, you can run into a variety of problems."

Skip to main content