My game window has manual resizing allowed, which means it can be resized like any other normal window, by dragging its edges. The game also makes use of a RenderTarget2D rt2d, to which the main Render Target is set in the main Draw method: GraphicsDevice.SetRenderTarget(rt2d), but it is reset back to null (the default render target) in the end of the main Draw method, which makes it a little bit confusing: is this really the source of the problem, resizing the game window right between the moment Render Target is set to rt2d, and not reset back to default? Right now it looks like it.
The code within the main Draw method is supposed to always reset the main Render Target back to null, so there are no expected cases when this normally wouldn't happen.
Still, the result of resizing the game window sometimes causes GraphicsDevice.isDisposed return true, and then the game throws System.ObjectDisposedException at the first SpriteBatch.End(). I've found posts about this error going back to the first days of XNA, but without a good explanation (and also without mentioning changing Render Target, so it may have been the source of the problem for those posters too).
Right now I'm able to trigger this error by calling this method a few times:
graphics.PreferredBackBufferWidth = graphics.PreferredBackBufferWidth;
graphics.PreferredBackBufferHeight = graphics.PreferredBackBufferHeight;
graphics.ApplyChanges();
…With the following lines in the main Draw method:
RenderTarget2D rt2d = new RenderTarget2D(GraphicsDevice,
graphics.PreferredBackBufferWidth,
graphics.PreferredBackBufferHeight);
GraphicsDevice.SetRenderTarget(rt2d);
sb.Begin();
// main draw method here, it's pretty big, so it might be taking long
// enough to process to actually resize before resetting render target
sb.End();
GraphicsDevice.SetRenderTarget(null);
sb.Begin();
// draw the whole rt2d to the screen
sb.End();
My guess is that I should be aborting the frame draw and resetting the render target if the resizing happens before the Render Target is reset, but I'm still not sure this is exactly what is causing this.
UPD: There are Window.ClientSizeChanged and graphics.PreparingDeviceSettings events, but even when they fire, defaulting the render target doesn't seem to help.
I guess this is not "timeout between resizing the client area and new graphics settings applying" or whatever. This is most likely caused by non-default render target.
And it's probably not that the render target size becomes different from new screen size, because this also throws exception when changing graphics device dimensions to the exact same values.
UPD2: I just tried making fullscreen toggling a pending operation, making the F11 set isFullscreenTogglePending to true and checking it in the beginning of the main Update method, and it didn't help at all. Then I figured out that previously fullscreen mode was also being toggled from the main Update method, only not at the very beginning, but halfway through the input update method, so it doesn't really matter where in the main Update method it is run, it still causes this error. Interestingly, the GraphicsDevice.isDisposed is false when the exception is thrown.
This is the exception message:
System.ObjectDisposedException occurred
Message=Cannot access a disposed object.
Object name: 'GraphicsDevice'.
Source=Microsoft.Xna.Framework
ObjectName=GraphicsDevice
StackTrace:
at Microsoft.Xna.Framework.Helpers.CheckDisposed(Object obj, IntPtr pComPtr)
at Microsoft.Xna.Framework.Graphics.BlendState.Apply(GraphicsDevice device)
at Microsoft.Xna.Framework.Graphics.GraphicsDevice.set_BlendState(BlendState value)
at Microsoft.Xna.Framework.Graphics.SpriteBatch.SetRenderState()
at Microsoft.Xna.Framework.Graphics.SpriteBatch.End()
at secret_project.Game1.Draw(GameTime gameTime) in P:\msvs projects\secret_project\Game1.cs:line 3310
InnerException:
It's at the spriteBatch.End() in the main Draw call.
How do I prevent this error?
Possibly related questions:
When I change vertical size of XNA game window to minimum, it throws ObjectDisposedException for spritebatch, why?
Is it possible to restore a GraphicsDevice if something goes wrong with it?
Two things:
1.
I'm not familiar with render targets... but maybe this will help? From MSDN:
"Render targets represent a linear area of display memory and usually reside in the display memory of the display card. Because of this, RenderTarget objects must be recreated when the device is reset."
2.
Besides that, I had a similar problem at one point. I was disposing of a texture at the end of the draw call. This would work fine, unless I tried to move the window around. Every once in a while, an ObjectDisposed exception would occur (for the texture) when ever I tried to move the game window. My best guess at the reasoning was that the update thread and draw thread would get misaligned, if only for a brief moment, and the texture would be called again before it had a chance to reset. I never found a way to stop the effect, other than just making sure the object was not disposed before attempting to draw.
Of course, our situations might be completely unrelated, but as a possible fix, just add a flag that will stop any draw calls when the window has been recently re-sized.
And if that does not solve it, hopefully it will help narrow down what the problem is not.
You should not create any graphics resources inside Draw call, like you did with RenderTarget2D. First of all creating of such resources is slow and should be done only once for a GraphicsDevice. Only Set calls should be inside the Draw method, as setting already created resource is much faster since they are already inside graphics device memory.
What should you do - is to move all graphics resources creation (including RenderTarget2D) inside LoadContent call and leave only Set methods inside Draw. The LoadContent method is called whenever the GraphicsDevice is recreated (for example, when you resizing the viewport). So all previously created resources will be recreated too.
I think your exception comes because you are recreating the graphics device while the draw method is active. You should only change the device settings in the update method once your game is running. Set some variable like a bool to true if the resolution should be changed, check that value in the update method and apply the new resolution there.
public class Game1 : Microsoft.Xna.Framework.Game
{
protected override void Update(GameTime gameTime)
{
if(resolutionChanged)
{
graphics.PreferredBackBufferHeight = userRequestedHeight;
graphics.PreferredBackBufferWidth = userRequestedWidth;
graphics.ApplyChanges();
}
// ...
}
// ...
}
Also I aggree with OpenMinded: Never create a resource on a per frame basis. Use the GraphicsDevicerManagers PreparingDeviceSettings event. It will be fired when the graphicsdevice is reset or recreated.
I know a lot of time passed but i was hitting this error consistently on our 8 years old game after the user resized the window 2 times in a row, after a gamecomponent providing Bloom effect was disabled. Turned out the Bloom component at the end of the Draw was setting a GraphicsDevice.Texture[1] and it wasn't resetting it to null at the end of the draw. This caused after the first resize to have the GraphicsDevice.Texture[1] disposed, but still set on the device. On the second resize the disposed texture was causing the GraphicsDevice to fail the reset. Setting Texture[1] to null before the reset, or after drawing the bloom-component fixed the problem.
Related
My question might seem silly to you, but I realized that moving my applications form makes the code inside it run slower. E.g. when I load a bitmap image and apply some image editing algorithms on it, it takes about 22 secs for the whole process to finish. But if I move the form during execution, it adds some 3-4 extra seconds to the elapsed time. I was able to spot the delay using a Stopwatch. So how can I get around this behaviour, if possible at all?
This is just an hypothesis that requires your investigation, as you didn't post any code and thus it is impossible to really know what is going on.
Most probably you move the boundaries of the image outside the screen. When you move in again, the windowing engine will do some draw calls on those rectangles to be redrawn. The same happens on resize when you enlarge but not when you shrink the window.
If this is the case, then you will not experience any extra draw calls as long as you don't cover/uncover areas of the image.
So this is not an answer but in your place I would override the Paint() method and log how many excess calls are made. Based on this, I'd search for a solution, such as suppress those calls like this:
public override void Paint()
{
if (algorithmRunning)
{
return; // suppress any further computations
}
base.Paint(); // do actual redraws
}
This code is just an example, you'll have to fix it according to the MSDN documentation.
What you should NOT do is just hook into the OnPaint() event, because then you'll still have the actual Paint() method called.
This might be a very simple question, but I searched and found no other way to do it. It doesn't make sense to redraw the background on every Draw. Is there a way to draw some things and leave them on the screen?
I've tried to comment-out the
GraphicsDevice.Clear(Color.CornflowerBlue);
But that doesn't help. (What is its purpose?)
The dark purple colour you are seeing is used by XNA and DirectX to indicate an uninitialised buffer. XNA will also clear buffers to this colour to emulate the behaviour of the Xbox 360 or Windows Phone, so that if you build a game on Windows, it "just works" on those other platforms (or, rather, so it fails in the same way, so you can debug it).
XNA is double-buffered. You don't draw directly to the screen, but to a "backbuffer". The screen only displays the "front buffer". Every time GraphicsDevice.Present gets called (Game calls it for you in EndDraw), those two buffers get swapped, and what you were drawing gets displayed (and you get a fresh buffer to draw on).
I'm not sure why XNA marks the buffer as uninitialised when it gets swapped. I haven't come across this behaviour before - mostly because it's very unusual to want to swap buffers and preserve their contents.
Usually what you want to do is call Game.SupressDraw, when you know you're not going to modify the contents of the screen (saving both a call to Draw and a swap). See also answers here and here.
Keep in mind that clearing the screen with GraphicsDevice.Clear is extremely fast. And that XNA has no concept of "background" or "foreground" (you're always drawing on top of whatever is already in the buffer).
If you do have some expensive-to-render content that you want to re-use between frames, generally you would draw it into to a render target once, and then draw that to the screen each frame. But, as always, avoid premature optimisation! Graphics cards are designed specifically to redraw scenes every frame - they're pretty damn fast!
See this, if you want to just prevent it clearing the image you can do:
GraphicsDevice.GetType().GetField("lazyClearFlags", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(GraphicsDevice, ClearOptions.DepthBuffer);
I have a simple OpenGL program written in c# using SharpGL.
The program takes a series of points from a file and renders them using the GLBegin/GLEnd method.
That part works fine. My problem arises when I try to change adjust the perspective of the rendering. If I change the size and position of the OpenGLControl, and then call:
public void AdjustPerspective(double width, double height)
{
_gl.MatrixMode(OpenGL.GL_PROJECTION);
_gl.LoadIdentity();
_gl.Perspective(45.0f, width / height, .1, 400.0);
_gl.MatrixMode(OpenGL.GL_MODELVIEW);
}
The render starts stretching out off the screen.
What it should look like:
Now, this doesn't always happen, and it only happens after calling the AdjustPerspective method.
The method is called from an event that is called when the OpenGLControl is resized.
I'm completely at a loss about this because it doesn't always happen. My first thought was that it had something to do with the near plane, but seeing as it doesn't always happen, that can't be it.
My only idea is that glPerspective has some adverse effects when it's called multiple times.
The draw calls are also called from an event, if it helps.
I ended up fixing it by setting RenderContextType to SharpGL.RenderContextType.NativeWindow when initializing the OpenGLControls
I am currently making a game with GDI+, I know it is not the optimal solution for developing a game, but since it is a school project I have no choice.
About every tenth time I run my game, the graphics gets rendered outside the form in the top left corner of the screen.
I'm using double buffering if that helps to narrow the problem down.
The rendering code looks like this:
while (true)
{
// Create buffer if it don't exist already
if (context == null)
{
context = BufferedGraphicsManager.Current;
this.buffer = context.Allocate(CreateGraphics(), this.DisplayRectangle);
}
// Clear the screen with the forms back color
this.buffer.Graphics.Clear(this.BackColor);
// Stuff is written to the buffer here, example of drawing a game object:
this.buffer.Graphics.DrawImage(
image: SpriteSheet,
destRect: new Rectangle(
this.Position.X
this.Position.Y
this.SpriteSheetSource.Width,
this.SpriteSheetSource.Height),
srcX: this.SpriteSheetSource.X,
srcY: this.SpriteSheetSource.Y,
srcWidth: this.SpriteSheetSource.Width,
srcHeight: this.SpriteSheetSource.Height,
srcUnit: GraphicsUnit.Pixel);
// Transfer buffer to display - aka back/front buffer swapping
this.buffer.Render();
}
It's easier to explain with a screenshot:
It was a bit of a design mistake in Winforms to make the BufferedGraphicsXxx classes public. They are an implementation detail of double-buffering support in Winforms and they are not terribly resilient to using them wrong.
You are definitely using the BufferedGraphics you get back from Allocate() wrong. You create buffers at a high rate, inside the game loop. But you forget to dispose the buffer you used at the end of the loop. This will consume device contexts (HDC's) at a high rate. That doesn't go on forever, if your program doesn't otherwise get the garbage collector running then Windows pulls the plug and will not let you create a new device context. The internal CreateCompatibleDC() call will fail and returns NULL. The BufferedGraphicsContext class otherwise misses the code to check for this error and plows on with the NULL handle. And starts painting to the desktop window instead of the form.
A fix will be to move the Allocate() call outside of the loop so you do it just once. But now you'll have a new problem when the user changes the window size, the buffer is no longer the correct size.
The better mousetrap is to just not use the BufferedGraphics class but leave it up to Winforms to get it right. There are several ways to get a gameloop in Winforms, but the simplest one is to just use the OnPaint() method to render the scene and immediately ask for another paint so it keeps getting called over and over again. Similar to this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e) {
RenderScene(e.Graphics);
this.Invalidate();
}
}
Where RenderScene() should draw the game objects, using the passed Graphics instance. Note that you no longer need to use Clear(), that was already done.
About every tenth time I run my game, the graphics gets rendered
outside the form in the top left corner of the screen.
From the screen shot and your description, you are occasionally drawing to the Window's Desktop device context (DC); Which is the effect of using a window handle of zero (IntPtr.Zero) when getting the DC.
This lead me to believe you could be starting the game loop before the form window has been created resulting in the graphics context to point to a zero'd window handle.
As confirmed in the commentary you are using a separate thread for your game loop resulting in the random behavior of this happening. Once dealing with threads, you don't always get the same result twice when it comes to timing of start up and completion of threads (especially when threads can run parallel, via a multi-core/cpu computer). Each time the game application is ran, there is a chance the game loop thread can start-up and execute before the form window on the UI thread can be created and shown.
I have a C# .NET application with which I've created a custom image display control. Each image display represents its own display context and draws the image using glDrawPixels (Yes I know it would be better to use textures, I plan to in the futures but this app is already too far along and my time is limited).
I am now trying to have both images pan simultaneously. That is, when one image is moved down ten pixels, the second image moves down ten pixels. Like so:
imageOne.YPan -= 10;
imageTwo.YPan -= 10;
imageOne.Invalidate(); //This forces a redraw.
imageTwo.Invalidate(); //This forces a redraw.
Alright so here is the problem I am having. Only one of the images displays is redrawing. If I place a pause in between the two Invalidate calls and make the pause duration at least 110 milliseconds both will redraw, but not simultaneously. So it looks as if the second image is always trying to catch up to the first. Plus, a 110 millisecond pause slows down the motion too much.
I have tried placing the updating and invalidating of each image in its own thread but this did not help.
At the beginning of drawing I make the appropriate context is current, and at the end I am calling swapbuffers(). I tried adding a glFinish to the end of the draw function, but there was no change.
Could it be that its the graphics card that is the problem? I am stuck using an integrated gpu that only has openGL 1.4.
Hopefully, I have provided enough detail that the answer to my problem can be found.
Its difficult telling what's wrong with what you do since you give so little detail. Here are some pointers which may help.
- before doing something in a context, make sure you make it the current one. If you want to pan two contexts, make the first one current, pan it and then make the second one current and pan it. These is no real reason why this should not work.
- If it looks like there is a timing problem, adding glFinish() at strategic places may help weed the problem out
- As should always be done, on occasions call glError() and see that everything went well.
- I'm not sure how this is done in the framework you're talking about but you should make sure that both contexts get a swapBuffers() call for every frame.
Invalidate doesn't force an immediate redraw. It marks the window invalid, and when the message queue runs out of other messages, a paint message will be created and processed. But that won't happen until you finish processing the current message and return to the main message loop, and it may be delayed even more than that.
Generally OpenGL animation is an exception to the rule of doing all drawing inside Control.OnPaint (or in a handler for the Control.Paint event).