Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw 2D pixel-by-pixel in XNA?

Tags:

c#

2d

xna

I'm trying to draw on the screen pixel-by-pixel using XNA, but am having problems with resources. I thought the best way would be to have 1 texture that updates every frame, but I'm having trouble updating it. Here's what I've got so far, just as a test:

Texture2D canvas;
Rectangle tracedSize;
UInt32[] pixels;

protected override void Initialize()
    {
        tracedSize = GraphicsDevice.PresentationParameters.Bounds;
        canvas = new Texture2D(GraphicsDevice, tracedSize.Width, tracedSize.Height, false, SurfaceFormat.Color);
        pixels = new UInt32[tracedSize.Width * tracedSize.Height];               

        base.Initialize();
    }

protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        pixels[100] = 0xFF00FF00;
        canvas.SetData<UInt32>(pixels, 0, tracedSize.Width * tracedSize.Height);

        spriteBatch.Begin();
        spriteBatch.Draw(canvas, new Rectangle(0, 0, tracedSize.Width, tracedSize.Height), Color.White);
        spriteBatch.End();

        base.Draw(gameTime);
    }

When Draw() is called the second time, I get the following error:

"The operation was aborted. You may not modify a resource that has been set on a device, or after it has been used within a tiling bracket."

If I try to make a new Texture2D in Draw(), I quickly get an out of memory error. This is for Windows Phone. It seems like I'm trying to do it the wrong way, what other options do I have to make it work?

like image 201
Curyous Avatar asked Jan 29 '11 14:01

Curyous


3 Answers

Try setting GraphicsDevice.Textures[0] = null before you call SetData. Depending on the effect you're after there may be a more performant method, you could also consider Silverlights WriteableBitmap.

Edit: This is the code I tested in the emulator:

Texture2D canvas;
Rectangle tracedSize;
UInt32[] pixels;

protected override void Initialize()
{
    tracedSize = GraphicsDevice.PresentationParameters.Bounds;
    canvas = new Texture2D(GraphicsDevice, tracedSize.Width, tracedSize.Height, false, SurfaceFormat.Color);
    pixels = new UInt32[tracedSize.Width * tracedSize.Height];

    base.Initialize();
}
Random rnd = new Random();
protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    GraphicsDevice.Textures[0] = null;
    pixels[rnd.Next(pixels.Length)] = 0xFF00FF00;
    canvas.SetData<UInt32>(pixels, 0, tracedSize.Width * tracedSize.Height);

    spriteBatch.Begin();
    spriteBatch.Draw(canvas, new Rectangle(0, 0, tracedSize.Width, tracedSize.Height), Color.White);
    spriteBatch.End();

    base.Draw(gameTime);
}
like image 121
Kris Avatar answered Sep 20 '22 17:09

Kris


You basically need to do as it asks in the exception:

To ensure that the texture is not set on the graphics device, put this at the end of Draw:

GraphicsDevice.Textures[0] = null;

To ensure you are not drawing inside a tiling bracket, do not use SetData inside of Draw at all. Move the call to SetData into Update.


Bonus info: Your out-of-memory error is because you are not releasing the unmanaged resources that Texture2D allocates (the garbage collector can't track them, so it doesn't know you're running out of memory). You need to call Dispose on the texture. However making a new texture each frame is a bad idea anyway (there's no way to avoid the performance and memory fragmentation problems it causes).

like image 39
Andrew Russell Avatar answered Sep 23 '22 17:09

Andrew Russell


Never create or modify a texture in Draw() ** ever.**

The SpriteBatch.Draw() call expects the GraphicsDevice buffer to contain all of the texture data (by extension to the GPU buffer), any changes still happening(from Update and IsRunningSlowly == true) at this point will cause tearing when rendered.

The workaround of GraphicsDevice.Textures[0] = null; blocks the Draw call and the game until transfer to the GPU is complete, thus slowing the entire game loop.

Reuse a Texure2D object declared at the class level.

like image 21
Strom Avatar answered Sep 22 '22 17:09

Strom