tags:

views:

41

answers:

3

Attempting to do SetData() on Texture2D that has been recently Draw()-n by SpriteBatch leads to following exception:

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.

Can I determine in advance if executing SetData() will throw this exception?

+1  A: 

Basically, you have three options:

1) See if SpriteBatch has finished it's operation and call SetData() afterwards. The drawing methods usually are asynchronous. That means that, they just get added to the please-render-me queue and the method returns immediately. What you need is either a callback notification when the drawing has finished or a synchronous call to Draw().

2) Avoiding that SetData() gets interrupted. You can do that by putting it into a critical section which I would not recommend. It should be possible to lock the texture data. It's called LockRect() in Direct3D, it should be similar in XNA.

3) There should be some method like Flush() somewhere that waits until all graphics-related operation have finished.

Sorry for the rather vague help, but you should be able to find the method names from the XNA doc.

Thank you. I got around this problem by allocating two textures instead of one and displaying one, but setting data to the other and switching them after that.
Kamil Szot
+1  A: 

Basically no.

The easiest thing to do is to only call SetData in your Update method.

It's bad practice to use SetData within your Draw method, as the device could be doing all kinds of voodoo magic with the old data. This is explained in detail in the "Caution" box on its MSDN page.

Now, up until XNA 3.1 you could use SetDataOptions with Texture2D.SetData. But it looks like that functionality has been removed in XNA 4.0 for textures.

Shawn Hargreaves explains here how SetDataOptions could be used tell the GPU "actually yes I do want to overwrite that data you might be using, don't complain". And why it's difficult to get right.

Andrew Russell
I'm pretty sure I had been using SetData from Update() method. I had only one thread and method that called SetData was called from Update(). It included some cpu and disk intensive operations so I wanted to have it outside of Draw()
Kamil Szot
Thanks for the hint about SetDataOptions ... seems interesting.
Kamil Szot
+1  A: 

I've solved a problem like this by creating two textures, and switching between the active one, essentially double-buffering:

void CreateTextures()
{

  depth_1 = new Texture2D(this.GraphicsDevice, width, height, false, SurfaceFormat.Single);
  depth_2 = new Texture2D(this.GraphicsDevice, width, height, false, SurfaceFormat.Single);

  depth_current = depth_1;

  ...

}

void Draw(GameTime gt)
{
  depth_current = depth_current == depth_1 ? depth_2 : depth_1;
  Depth.SetData(this.DepthBuffer);

  ...

}

In my case it wasn't possible to move the SetData outside of Draw, but I suppose that's the nicest way to do it.

JulianR