BoxingViewportAdapter issue when using a RenderTarget2D

Hello everyone :slight_smile:

I’m currently trying to figure out why the BoxingViewportAdapter doesn’t work as expected when I’m using a RenderTarget2D. The simple fact of calling GraphicsDevice.SetRenderTarget() will make the boxing viewport adapter have the same results than if I used the scaling viewport adapter, and I don’t understand why :’(

Here is a quick example, I’ve just updated the Demo.ViewportAdapters project to only use a BoxingViewportAdapter and to create and clear a RenderTarget2D (without using it anyway):

You can see that commenting the line 56, it works as expected whereas it shouldn’t have any impact:

_graphics.GraphicsDevice.SetRenderTarget(_renderTarget);

Do you know what happens?

Internally the BoxingViewportAdapter signs up to the ClientSizeChanged event of the GameWindow. This is how it detects changes in window size and adjusts the viewport accordingly.

I suspect what’s happening is that when you switch to the render target, the GraphicsDevice.Viewport is changing size without firing the ClientSizeChanged event. Or perhaps it is firing, but the viewport is no longer the same size as the window? Not sure.

These types of bugs can be quite fiddly to debug. The first thing I would try is calling the Reset method manually on the viewport adapter after switching the render target.

If that doesn’t work, you might need to sit with the debugger for a while and inspect the values coming from the Viewport property of the GraphicsDevice. If it’s changing unexpectedly, you might be able to work around the issue by storing and restoring the viewport size manually.

It could be a bug in MonoGame.Extended or perhaps in MonoGame. Once you get more familiar with it, it’s probably a good idea to raise an issue on github.

1 Like

Thanks a lot for your answer @craftworkgames!

As you suspected, the ClientSizeChanged event is not triggered when we switch to the render target whereas the GraphicsDevice.Viewport is updated to the render target size (32x32 in my example code). Then, when we go back to the back buffer (GraphicsDevice.SetRenderTarget(null)), the viewport is also updated, but with the wrong values, instead of taking the previous ones, it will take the entire screen.

Calling ViewportAdapter.Reset() just after switching to the back buffer does the job, thank again :slight_smile:

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

    // Clear the render target
    _graphics.GraphicsDevice.SetRenderTarget(_renderTarget);
    _graphics.GraphicsDevice.Clear(Color.Red);
    _graphics.GraphicsDevice.SetRenderTarget(null);
       
    // Reset the viewport
    _viewportAdapter.Reset();

    var destinationRectangle = new Rectangle(0, 0, _virtualResolution.X, _virtualResolution.Y);

    _spriteBatch.Begin(transformMatrix: _viewportAdapter.GetScaleMatrix());

    _spriteBatch.Draw(_backgroundTexture, destinationRectangle, Color.White);
    _spriteBatch.DrawString(_bitmapFont, $"Current: {_viewportAdapter.GetType().Name}", new Vector2(49, 40 + _bitmapFont.LineHeight * 4), Color.Black);
    _spriteBatch.DrawString(_bitmapFont, @"Try resizing the window", new Vector2(49, 40 + _bitmapFont.LineHeight * 6), Color.Black);

    _spriteBatch.End();

    base.Draw(gameTime);
}
1 Like