7
\$\begingroup\$

In my Monogame (XNA) Windowed App, compiled for Windows PC, running on Windows 7, I have

public RenderTarget2D renderTarget = null;

In the Inititate() function I have

renderTarget = new RenderTarget2D(graphicsDevice, 200, 200, false,
                           graphicsDevice.PresentationParameters.BackBufferFormat,
                           graphicsDevice.PresentationParameters.DepthStencilFormat);

My drawing is as follows

public override void Draw(GameTime gameTime)
{
    base.Draw(gameTime);

    // POSITION A

    // draw some stuff to the back buffer
    graphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer,
                         backgroundColor, 1, 0);

    SceneRoot.Draw(gameTime);    // draw some 3d primitives

    spriteBatch.Begin();
    spriteBatch.Draw(...some things...);
    spriteBatch.End();

    // POSITION B

    spriteBatch.Begin();
    spriteBatch.Draw((Texture2D)renderTarget, new Vector2(5f, 200f),
                     new Rectangle(0, 0, renderTarget.Width, renderTarget.Height),
                     Color.White);
    spriteBatch.End();
}

If I put the following code to draw to the renderTarget in POSITION A then I can see the stuff drawn between POSITION A and POSITION B, with the stuff drawn to the renderTarget on top, as expected.

If I put the following code in POSITION B then I can't see anything drawn between POSITION A and POSITION B, just the stuff drawn to the renderTarget.

graphicsDevice.SetRenderTarget(renderTarget);
graphicsDevice.Clear(Color.Red);
spriteBatch.Begin();
spriteBatch.Draw(...some things...);
spriteBatch.End();
graphicsDevice.SetRenderTarget(null);

With the above code for insertion, I can even reduce it to

graphicsDevice.SetRenderTarget(renderTarget);
graphicsDevice.SetRenderTarget(null);

and still get the same result (only see what is drawn to the renderTarget, which is now nothing, ie a blank screen). I've tested, and it's the SetRenderTarget(renderTarget) that's causing the problem, seemingly by wiping the backbuffer.

Oh, and none of the sprites drawn cover the whole display, so it's not the sprites displaying wrong, I don't think, as I should still see the 3d stuff.

I've looked and I don't see anything in SetRenderTarget about wiping the backbuffer. Can anyone help? Is it something else, like something to do with the RenderState or the Effects? I can provide more of my code if need be.

\$\endgroup\$
3
  • \$\begingroup\$ This is also posted to stackoverflow.com/questions/27678972/… - should I delete that post? \$\endgroup\$ Commented Dec 29, 2014 at 2:01
  • 1
    \$\begingroup\$ You should delete one or the other, yes. \$\endgroup\$ Commented Dec 29, 2014 at 2:40
  • \$\begingroup\$ Thanks, deleted the other... I was uncertain whether these are considered connected (and thus mutually exclusive), as my rep and stats there did not carry over to here, though it seemed like it should \$\endgroup\$ Commented Dec 29, 2014 at 4:01

2 Answers 2

5
\$\begingroup\$

I am not 100% sure about the Monogame implementation. But, in XNA, what you are seeing is the correct behaviour. One would expect Monogame to be faithful to the XNA implementation.

The reason this behaviour exists is that on some devices (Xbox 360, WinPhone) the operations for saving and restoring a backbuffer are slow. So the default is to clear a render target (including the backbuffer via null) whenever it is set with SetRenderTarget. Even on hardware that does not require a clear, it is cleared anyway so that your code works cross-platform.

The behaviour is controlled by passing RenderTargetUsage (MSDN) to the RenderTarget2D constructor (MSDN) or setting it in your PreparingDeviceSettings handler (MSDN; the thing to set is args.GraphicsDeviceInformation.PresentationParameters.RenderTargetUsage).

But, rather than modifying the RenderTargetUsage, it is generally better to simply draw to your render targets in an order that respects dependencies.

(And, yes, the XNA documentation is terrible for not mentioning this anywhere.)

\$\endgroup\$
5
  • \$\begingroup\$ Hi Andrew, thanks for the reply. I can quite see that ideally it's best to just shuffle the order of drawing to avoid this, and I will - but I would like to have the option to do it later in case I have the need in this or future projects (I'm making a personal library and this could come up again). \$\endgroup\$ Commented Dec 31, 2014 at 5:22
  • \$\begingroup\$ I tried modifying my RenderTarget creation to renderTarget = new RenderTarget2D(graphicsDevice, 200, 200, false, graphicsDevice.PresentationParameters.BackBufferFormat, graphicsDevice.PresentationParameters.DepthStencilFormat, graphics.PresentationParameters.MultiSampleCount, RenderTargetUsage.PreserveContents); and I still get the backbuffer wiped. Any thoughts? \$\endgroup\$ Commented Dec 31, 2014 at 5:24
  • \$\begingroup\$ Oh, and thanks for clearing up that this is the expected behaviour - it was driving me crazy thinking I was missing something in all of this! \$\endgroup\$ Commented Dec 31, 2014 at 7:03
  • 1
    \$\begingroup\$ @James No worries. Regarding your second comment there: You have to set PreserveContents on the backbuffer to avoid the clear of the backbuffer during SetRenderTarget(null). The code you have there only avoids the clear of the render target when you call SetRenderTarget(renderTarget). \$\endgroup\$ Commented Jan 1, 2015 at 2:31
  • \$\begingroup\$ Thanks Andrew, that solved it. To clarify, I put graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(SetToPreserve); in my game creation function MyGame(), and then added the function void SetToPreserve(object sender, PreparingDeviceSettingsEventArgs eventargs) { eventargs.GraphicsDeviceInformation.PresentationParameters.RenderTargetUsage = RenderTargetUsage.PreserveContents; } - that did exactly what I was after. You are the man, many thanks. \$\endgroup\$ Commented Jan 1, 2015 at 7:01
3
\$\begingroup\$

I'm going to post this answer as an adaptation from @James Carlyle-Clarke's comment. Because the code formatting in an answer makes it easier to understand. It has also been modified to make it shorter.

You should place this in your Game Object's constructor, after setting a new GraphicsDeviceManager.

graphics.PreparingDeviceSettings += (object s, PreparingDeviceSettingsEventArgs args) =>
{
    args.GraphicsDeviceInformation.PresentationParameters.RenderTargetUsage = RenderTargetUsage.PreserveContents;
};
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .