Mincus's picture

OpenTK and IDisposable

I have been wondering about whether OpenTK expects unmanaged resources to use IDisposable.
Largely up until now I've just been ensuring to release all resources in the Unload() function for Gamewindow. This works well enough when you keep track of all the resources, but no-one's perfect so I was looking at IDisposable.
On the surface it looks like a good plan, but having read this thread (which doesn't appear to have a solution), it brings up the question "what if the OpenGL context has been destroyed?"

My question is basically this: Should OpenTK library users be implementing the IDisposable pattern or does it really not matter?
The reason for this is I'm intending on writing a short tutorial series to get new users familiar with the Gamewindow class, using VBOs and eventually extending it to a small game (or two) building up a simple toolkit (I had been intending this before Fiddler made the post and have the first tutorial largely ready, I'll post the first two shortly in a blog post for comments). Including the IDisposable pattern in such a tutorial would make sense if OpenTK expects it, but I also don't want to make the tutorials too complicated, and IDisposable is a fairly complicated pattern to understand some of the reasoning behind (I refer to this codeproject article as a good reference for understanding it, with this MSDN article explaining how it should be implemented).

So I knocked up a fairly simple example of how I suspect IDisposable would fit into OpenTK. It doesn't cause any problems, but I also know that doing things this way without IDisposable also won't cause any problems (whether you forget to call Unload on the texture or not).

	public class TextureClass : IDisposable
	{
		private sealed class TextureWrapper : IDisposable
		{
			public TextureWrapper(int textureID)
			{
				TextureID = textureID;
				Disposed = false;
			}
 
			public int TextureID;
 
			public bool Disposed;
 
			public void Dispose ()
			{
				if(!Disposed)
				{
					GC.SuppressFinalize(this);
					GL.DeleteTexture(TextureID);
					Disposed = true;
				}
			}
		}
 
		public TextureClass(string filename)
		{
			Filename = filename;
 
			Disposed = false;
		}
 
		private string Filename;
 
		private TextureWrapper Texture = null;
 
		public bool Disposed;
 
		public int ID { get { return Texture.TextureID; } }
 
		public void Load()
		{
			Bitmap TextureBMP = new Bitmap(Filename);
			int handle = 0;
 
            BitmapData TextureData = TextureBMP.LockBits(new Rectangle(0, 0, TextureBMP.Width, TextureBMP.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
            GL.GenTextures(1, out handle);
            GL.BindTexture(TextureTarget.Texture2D, handle);
            GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (float)TextureEnvMode.Modulate);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (float)TextureMinFilter.LinearMipmapLinear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (float)TextureMagFilter.Linear);
 
            Glu.Build2DMipmap(TextureTarget.Texture2D, (int)PixelInternalFormat.Srgb8Alpha8, TextureBMP.Width, TextureBMP.Height, OpenTK.Graphics.PixelFormat.Bgra, PixelType.UnsignedByte, TextureData.Scan0);
 
			Texture = new TextureWrapper(handle);
 
			Disposed = false;
		}
 
		public void Unload()
		{
			Dispose();
		}
 
		public void Dispose()
		{
			if(!Disposed)
			{
				GC.SuppressFinalize(this);
				if(Texture != null)
				{
					Texture.Dispose();
				}
				Disposed = true;
			}
		}
	}

Full solution with simple use case is attached (expects logo.jpg from the OpenTK examples and works with OpenTK 0.9.8).

AttachmentSize
disposable example.zip3 KB

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
martinsm's picture

For my last game I was making I was using GLFW, running on win, linux and macosx. Reopening window there was only way to change multisampling sample count (0x, 2x, 4x, ...) in game options. At least I know that on Windows it is not possible to SetPixelFormat with different pixel format index to same DC handle. Don't know about other OS.

the Fiddler's picture

@Objarni: this feature is far from useless. You listed some important issues but, as it happens, they are either solved or in progress: GL3 is very well-tested and works flawlessly; cross-platform compatibility is great (there are just three known issues and two of those are being worked on); drupal 6 will solve the author issue.

Changing FSAA is a good use case. On WinForms you can call GLControl.RecreateHandle to recreate the control & context using the new mode and GameWindow should probably offer something similar. If we had a RecreateHandle method, we could solve the Close() issue by defining the following sequence as valid:

GameWindow window = new GameWindow();
window.Close();
// Calling any GameWindow method now throws an ObjectDisposedException
window.RecreateHandle();
// Calling any GameWindow method now works.

I don't see how context pooling would help here. Besides being very fragile and relying on platform-specific behavior, how could it possibly impact the behavior of closed GameWindows?

objarni's picture

I don't see how context pooling would help here. Besides being very fragile and relying on platform-specific behavior, how could it possibly impact the behavior of closed GameWindows?

I thought this thread was about IDisposable behaviour in combination with contexts/textures/etc.?

Re-opening GameWindows is an even more esoteric issue than re-opening WinForms.

The pooling idea has the potential to deprecate the need of complicated disposing-behaviour, ridding OpenTK users of this hassle. And OpenTK developers too.. But since I don't know enough of the lower-level details of contexts, I don't know if the approach is at all possible, let alone easy to implement.

objarni's picture

OK I must confess my knowledge of the IDisposable pattern was less-than-optimal, and still is. I'm reading through Mincus links in the initial post, and it is much more complicated than I imagined, and in fact there seems to be fundamental problems with the .NET system in this regard, something I didn't know before and have to ponder to fully understand.

Please ignore any of my above posts since I was not aware of my unawereness ;)

Mincus's picture

Sorry, was my fault a bit, I pushed things off topic since I considered my question answered and it seemed like there was a related issue with the window closing.
And on that I hadn't considered martinism's situation, which would be solved in a similar way to Fiddler's handle recreation I feel.

objarni: Be careful with the codeproject article, the guy puts in a fair bit of personal opinion on MS decisions that isn't relevant to what the article teaches. It's still a good article, but you do have to take the personal opinions with a pinch of salt. :o)
And IDisposable is far more complicated than I realised as well, realising that is why I posted this in the first place.