Welcome to MSDN Blogs Sign in | Join | Help

Sixty fractals per second

The Xbox GPU is a shading monster!

I've written several Mandelbrot viewers over the years, but this is the first time I've ever been able to move around this at a rock solid 60 frames per second:

The trick to making this fast is to do all the heavy lifting on the GPU. I'm computing the fractal entirely inside my pixel shader, using this Mandelbrot.fx effect file:

    #define Iterations 128

float2 Pan;
float Zoom;
float Aspect;

float4 PixelShader(float2 texCoord : TEXCOORD0) : COLOR0
{
float2 c = (texCoord - 0.5) * Zoom * float2(1, Aspect) - Pan;
float2 v = 0;

for (int n = 0; n < Iterations; n++)
{
v = float2(v.x * v.x - v.y * v.y, v.x * v.y * 2) + c;
}

return (dot(v, v) > 1) ? 1 : 0;
}

technique
{
pass
{
PixelShader = compile ps_3_0 PixelShader();
}
}

Since the GPU is doing all the work, my C# code is pretty simple. Starting with the default Xbox 360 Game project, you need to add a few fields:

    Effect mandelbrot;
SpriteBatch spriteBatch;
Texture2D dummyTexture;

Vector2 pan = new Vector2(0.25f, 0);
float zoom = 3;

Add this to the LoadGraphicsContent method:

    mandelbrot = content.Load<Effect>("Mandelbrot");

spriteBatch = new SpriteBatch(graphics.GraphicsDevice);

int w = graphics.GraphicsDevice.Viewport.Width;
int h = graphics.GraphicsDevice.Viewport.Height;

dummyTexture = new Texture2D(graphics.GraphicsDevice, w, h, 1,
ResourceUsage.None, SurfaceFormat.Color);

I'm using a bit of a trick here. To render my fractal, I want to draw a fullscreen quad using my custom pixel shader. SpriteBatch provides an easy way to draw fullscreen quads, but it expects to be given a source texture. I don't need any source texture for my fractal, but I'm creating the dummyTexture as a trick to keep SpriteBatch happy. Yes, that's a nasty hack, and I apologise for it :-)

My Update method uses the gamepad to zoom and pan the display:

    GamePadState pad = GamePad.GetState(PlayerIndex.One);

if (pad.Buttons.A == ButtonState.Pressed)
zoom /= 1.05f;

if (pad.Buttons.B == ButtonState.Pressed)
zoom *= 1.05f;

float panSensitivity = 0.01f * (float)Math.Log(zoom + 1);

pan += new Vector2(pad.ThumbSticks.Left.X, -pad.ThumbSticks.Left.Y) * panSensitivity;

And finally, my Draw method issues a single SpriteBatch call to draw a fullscreen quad, using my Mandelbrot effect to apply all that massively parallel Xbox GPU goodness to every pixel of the screen:

    GraphicsDevice device = graphics.GraphicsDevice;

float aspectRatio = (float)device.Viewport.Height / (float)device.Viewport.Width;

mandelbrot.Parameters["Pan"].SetValue(pan);
mandelbrot.Parameters["Zoom"].SetValue(zoom);
mandelbrot.Parameters["Aspect"].SetValue(aspectRatio);

spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);
mandelbrot.Begin();
mandelbrot.CurrentTechnique.Passes[0].Begin();

spriteBatch.Draw(dummyTexture, Vector2.Zero, Color.White);

spriteBatch.End();
mandelbrot.CurrentTechnique.Passes[0].End();
mandelbrot.End();

This is not only the fastest Mandelbrot renderer I've ever written, but probably also the least code!

Published Monday, December 11, 2006 5:24 PM by ShawnHargreaves

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

Tuesday, December 12, 2006 10:14 AM by JoelMartinez

# re: Sixty fractals per second

wow, good stuff ... I always hear about people who got their start programming by typing out code from magazines.  Then I hear said people lamenting the fact that programming is so complex these days that it would never fit in a magazine for the next generation of programmers to learn from.

This however, could easily be printed in a magazine :-D

Wednesday, December 13, 2006 9:39 AM by PukiWiki/TrackBack 0.2

# Blog/2006-12-13

2006-12-13 &dagger; 2006-12-13 Microsoft Robotic Studio 1.0 ???????? XNA Game Studio Express 1.0 &uarr;Microsoft Robotic Studio 1.0 &dagger; ?????????????????????????? ?????????????????????????? http://msdn.microsoft.com/robotics/downl...

Wednesday, December 13, 2006 5:03 PM by Darksides - Hack-1 Project

# Hack-1 Looking Back / Looking Forward

Looking back In the beginning there are darkness..., damn stop , too far back Looking back to before...

Thursday, December 14, 2006 5:36 PM by hordurj

# re: Sixty fractals per second

Very nice example, and easy to play with. Here is an example of where the shader uses the iteration counts to create a grayscale image.

float4 PixelShader(float2 texCoord : TEXCOORD0) : COLOR0    

{

   float2 c = (texCoord - 0.5) * Zoom * float2(1, Aspect) - Pan;  

float2 v = 0;

int n = 0;

for (n = 0; n < Iterations && dot(v,v)<4; n++)        

{            

v = float2(v.x * v.x - v.y * v.y, v.x * v.y * 2) + c;        

}        

return (dot(v, v) > 4) ? (float)n / (float)Iterations : 0;

}

Wednesday, December 20, 2006 7:34 AM by Hunter767

# re: Sixty fractals per second

Any ideas why I get this error?

System.InvalidOperationException

Both a valid vertex shader and  pixel shader (or valid effect) must be set on the device before draw operations may be performed.

Running on XP btw, haven't tried 360

Wednesday, January 03, 2007 12:15 PM by mamue

# re: Sixty fractals per second

Hunter767,

it seems like your graphics card doesn't support 3.0 pixel shaders. I had the exact same problem, and it took me a while to figure it out (the exception message is not very helpful)..

Try to change the "compile ps_3_0" directive (last line in the fx file) to ps_2_a or even ps_2_0. You'll also have to decrease the number of iterations (first line of the fx file). The maximum number of iterations on my machine is 32, and even then it's painfully slow - time to get a new video card I guess..

Thursday, January 04, 2007 7:01 PM by lloigor

# re: Sixty fractals per second

Having received the same System.InvalidOperationException error, I'm slightly puzzled as my graphic card is pixel shader 3.0 capable.  To double check this, I've interrogated the MaxPixelShaderProfile GraphicsDeviceCapability of my graphics adapter which correctly returns PS_3_0.

Having said this, if I drop to PS_2_a and 64 iterations (as suggested by mamue) it runs up without error.

As a result, and since I’m new to this type of development work, I wondered if anyone could provide any wisdom as to why the Pixel Shader 3 setting isn't playing ball.

Thanks in advance

Monday, June 15, 2009 3:32 PM by ben_griffiths

# re: Sixty fractals per second

Well this was an odd experience. Search google for XNA fractals, get an example working, toggle back to the webpage you were reading to see if there is anything else interesting here and realise that you're my best friend's (Ben) brother! Surreal is probably the best word for it.

Thanks for a great article though.

Tuesday, October 27, 2009 6:58 AM by Trinnexx

# re: Sixty fractals per second

ResouceUsage.none?

I take it by the date this is for older versions of xna. I imagine TextureUsage replaces resouce usage?

I changed it to TextureUsage but all I get is a black screen sadly.

Tuesday, October 27, 2009 9:49 AM by ShawnHargreaves

# re: Sixty fractals per second

Yes, ResourceUsage should be changed to TextureUsage in more recent versions of the XNA Framework.

I haven't tried this code recently, but I would expect it to still work. Are you running this on Xbox or PC? (if you're on PC, you may have to change the shader model to ps_2_0).

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
 
Page view tracker