Instead of typing a bunch of paragraphs, I may as well just post the video of the bug here.
(Yes, before you ask, I know I said I refuse to use Windows. But, I never said anything about Windows 7, which is far better than any Windows version this decade has brought us.)
Anyway, here is the code for the UIManager class which deals with rendering top level controls (and the red background in the game’s debug scene):
//EDITOR'S NOTE: GraphicsContext is merely a wrapper for SpriteBatch and GraphicsDevice so that said objects can be passed around as a single concise object.
//
//It also has built-in scissortesting and methods for drawing rectangles, circles, lines and text.
public void OnFrameDraw(GameTime time, GraphicsContext ctx)
{
ctx.Batch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied,
SamplerState.LinearWrap, DepthStencilState.Default,
RasterizerState.CullNone);
ctx.Clear(Color.Red);
ctx.Batch.End();
foreach (var ctrl in _topLevels)
{
ctx.Device.SetRenderTarget(ctrl.RenderTarget);
ctrl.Control.Draw(time, ctx, ctrl.RenderTarget);
ctx.Device.SetRenderTarget(_plexgate.GameRenderTarget);
ctx.Batch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied,
SamplerState.LinearWrap, DepthStencilState.Default,
RasterizerState.CullNone);
ctx.DrawRectangle(ctrl.Control.X, ctrl.Control.Y, ctrl.Control.Width, ctrl.Control.Height, ctrl.RenderTarget, Color.White);
ctx.Batch.End();
}
ctx.Batch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied,
SamplerState.LinearWrap, DepthStencilState.Default,
RasterizerState.CullNone);
var fps = Math.Round(1 / time.ElapsedGameTime.TotalSeconds);
ctx.DrawString($"FPS: {fps}", 0, 0, Color.White, new System.Drawing.Font("Lucida Console", 12F), TextAlignment.TopLeft);
ctx.Batch.End();
}
public void OnGameUpdate(GameTime time)
{
var mouse = Mouse.GetState();
foreach(var ctrl in _topLevels)
{
var w = ctrl.Control.Width;
var h = ctrl.Control.Height;
bool makeTarget = false;
if (ctrl.RenderTarget == null)
makeTarget = true;
else
{
if(ctrl.RenderTarget.Width != w || ctrl.RenderTarget.Height != h)
{
makeTarget = true;
}
}
if (makeTarget)
{
ctrl.RenderTarget = new RenderTarget2D(_plexgate.GraphicsDevice, ctrl.Control.Width, ctrl.Control.Height, false, _plexgate.GraphicsDevice.PresentationParameters.BackBufferFormat, _plexgate.GraphicsDevice.PresentationParameters.DepthStencilFormat, 1, RenderTargetUsage.PreserveContents);
ctrl.Control.Invalidate();
}
ctrl.Control.SetTheme(_thememgr.Theme);
ctrl.Control.Update(time);
}
//Propagate mouse events.
foreach(var ctrl in _topLevels)
{
if (ctrl.Control.PropagateMouseState(mouse.LeftButton, mouse.MiddleButton, mouse.RightButton))
break;
}
}
And the rendering + updating code for Control
- responsible for the actual rendering of the window border - and where the AccessViolationException
is thrown.
//_rendertarget = an instance member of the Control class. Used as a way of double-buffering the control itself to improve performance.
public void Draw(GameTime time, GraphicsContext gfx, RenderTarget2D currentTarget)
{
if (_invalidated)
{
if (_resized)
{
_rendertarget = new RenderTarget2D(gfx.Device, _width, _height, false, gfx.Device.PresentationParameters.BackBufferFormat, gfx.Device.PresentationParameters.DepthStencilFormat, 1, RenderTargetUsage.PreserveContents);
//_resized = false;
}
int lastw = gfx.Width;
int lasth = gfx.Height;
gfx.Width = _width;
gfx.Height = _height;
gfx.Device.SetRenderTarget(_rendertarget);
gfx.Batch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied,
SamplerState.LinearWrap, DepthStencilState.Default,
RasterizerState.CullNone);
OnPaint(time, gfx, _rendertarget);
gfx.Batch.End();
gfx.Width = lastw;
gfx.Height = lasth;
_invalidated = false;
}
foreach (var ctrl in _children)
{
int lastw = gfx.Width;
int lasth = gfx.Height;
int lastx = gfx.X;
int lasty = gfx.Y;
gfx.Width = ctrl.Control._width;
gfx.Height = ctrl.Control._height;
gfx.Device.SetRenderTarget(ctrl.RenderTarget);
ctrl.Control.Draw(time, gfx, ctrl.RenderTarget);
gfx.Device.SetRenderTarget(_rendertarget);
gfx.Batch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied,
SamplerState.LinearWrap, DepthStencilState.Default,
RasterizerState.CullNone);
gfx.DrawRectangle(ctrl.Control.X, ctrl.Control.Y, ctrl.Control.Width, ctrl.Control.Height, ctrl.RenderTarget, Color.White);
gfx.Batch.End();
gfx.Width = lastw;
gfx.Height = lasth;
gfx.X = lastx;
gfx.Y = lasty;
}
gfx.Device.SetRenderTarget(currentTarget);
gfx.Batch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied,
SamplerState.LinearWrap, DepthStencilState.Default,
RasterizerState.CullNone);
gfx.DrawRectangle(0, 0, _rendertarget.Width, _rendertarget.Height, _rendertarget, Color.White);
gfx.Batch.End();
/*try
{
byte[] data = new byte[(_rendertarget.Width * 4) * _rendertarget.Height];
_rendertarget.GetData<byte>(data); //accessviolation occurs here.
}
catch { }*/
}
public void Update(GameTime time)
{
if (_rendertarget == null)
{
_invalidated = true;
_resized = true;
}
//Poll the mouse state.
var mouse = Mouse.GetState();
//For toplevels, set mouse input loc directly.
int _newmousex = 0;
int _newmousey = 0;
if (Parent == null)
{
_newmousex = X + mouse.X;
_newmousey = Y + mouse.Y;
}
//For controls with parents, poll mouse information from the parent.
else
{
_newmousex = X + Parent._mousex;
_newmousey = Y + Parent._mousey;
}
if(_newmousex != _mousex || _newmousey != _mousey)
{
_mousex = _newmousex;
_mousey = _newmousey;
MouseMove?.Invoke(this, new Vector2(_newmousex, _newmousey));
_invalidated = true;
}
OnUpdate(time);
foreach (var child in _children)
{
bool makeTarget = false;
if (child.RenderTarget == null)
makeTarget = true;
else
{
if (child.RenderTarget.Width != child.Control.Width || child.RenderTarget.Height != child.Control.Height)
makeTarget = true;
}
if (makeTarget)
{
if(_rendertarget != null)
{
child.RenderTarget = new RenderTarget2D(_rendertarget.GraphicsDevice, child.Control.Width, child.Control.Height, false, _rendertarget.Format, _rendertarget.DepthStencilFormat, 1, RenderTargetUsage.PreserveContents);
child.Control.Invalidate();
}
}
child.Control.Update(time);
}
}
I’m running MonoGame 3.6, on Windows 7 (64-bit). This is a DesktopGL project.
Any help would be greatly appreciated!
Also, as said in the video, the rendertarget that’s throwing the error seems fine when I inspect it in the Locals window. Weird.