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?
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);
}
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With