From 62da31df4838f25eba2503a83ed62dd9dac085b9 Mon Sep 17 00:00:00 2001 From: the_fiddler Date: Sun, 23 Nov 2008 20:17:50 +0000 Subject: [PATCH] Made ContextHandle a struct to reduce GC pressure (ContextHandles are created per frame). Added xml documentation for the ContextHandle. Made the casts between ContextHandles and IntPtrs explicit. Updated all ContextHandle consumers to reflect the explicit cast. --- Source/OpenTK/Audio/AudioContext.cs | 11 +- Source/OpenTK/Audio/OpenAL/Alc/Alc.cs | 16 +- Source/OpenTK/ContextHandle.cs | 183 ++++++++++++++---- Source/OpenTK/Platform/DummyGLContext.cs | 2 +- .../OpenTK/Platform/Windows/WinGLContext.cs | 22 +-- Source/OpenTK/Platform/X11/Bindings/Glx.cs | 6 + Source/OpenTK/Platform/X11/X11GLContext.cs | 16 +- 7 files changed, 183 insertions(+), 73 deletions(-) diff --git a/Source/OpenTK/Audio/AudioContext.cs b/Source/OpenTK/Audio/AudioContext.cs index bec96864..94b76e6e 100644 --- a/Source/OpenTK/Audio/AudioContext.cs +++ b/Source/OpenTK/Audio/AudioContext.cs @@ -24,7 +24,8 @@ namespace OpenTK.Audio bool disposed; bool is_processing; - ContextHandle device_handle, context_handle; + IntPtr device_handle; + ContextHandle context_handle; bool context_exists; string device_name; @@ -298,7 +299,7 @@ namespace OpenTK.Audio context_handle = Alc.CreateContext(device_handle, attributes.ToArray()); - if (context_handle == IntPtr.Zero) + if (context_handle == ContextHandle.Zero) { Alc.CloseDevice(device_handle); throw new AudioContextException("The audio context could not be created with the specified parameters."); @@ -351,7 +352,7 @@ namespace OpenTK.Audio { lock (audio_context_lock) { - if (!Alc.MakeContextCurrent(context != null ? (IntPtr)context.context_handle : IntPtr.Zero)) + if (!Alc.MakeContextCurrent(context != null ? context.context_handle : ContextHandle.Zero)) throw new AudioContextException(String.Format("ALC {0} error detected at {1}.", Alc.GetError(context != null ? (IntPtr)context.context_handle : IntPtr.Zero).ToString(), context != null ? context.ToString() : "null")); @@ -395,7 +396,7 @@ namespace OpenTK.Audio #region IntPtr Device - IntPtr Device { get { return device_handle.Handle; } } + IntPtr Device { get { return device_handle; } } #endregion @@ -583,7 +584,7 @@ namespace OpenTK.Audio if (this.IsCurrent) this.IsCurrent = false; - if (context_handle != IntPtr.Zero) + if (context_handle != ContextHandle.Zero) { available_contexts.Remove(context_handle); Alc.DestroyContext(context_handle); diff --git a/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs b/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs index 31e610d4..55e7150f 100644 --- a/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs +++ b/Source/OpenTK/Audio/OpenAL/Alc/Alc.cs @@ -89,7 +89,7 @@ namespace OpenTK.Audio /// Returns a pointer to the new context (NULL on failure). The attribute list can be NULL, or a zero terminated list of integer pairs composed of valid ALC attribute tokens and requested values. [DllImport(Alc.Lib, EntryPoint = "alcCreateContext", ExactSpelling = true, CallingConvention = Alc.Style), SuppressUnmanagedCodeSecurity] [CLSCompliant(false)] - unsafe public static extern IntPtr CreateContext([In] IntPtr device, [In] int* attrlist); + unsafe public static extern ContextHandle CreateContext([In] IntPtr device, [In] int* attrlist); // ALC_API ALCcontext * ALC_APIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist ); /// This function creates a context using a specified device. @@ -97,7 +97,7 @@ namespace OpenTK.Audio /// an array of a set of attributes: ALC_FREQUENCY, ALC_MONO_SOURCES, ALC_REFRESH, ALC_STEREO_SOURCES, ALC_SYNC /// Returns a pointer to the new context (NULL on failure). /// The attribute list can be NULL, or a zero terminated list of integer pairs composed of valid ALC attribute tokens and requested values. - public static IntPtr CreateContext(IntPtr device, int[] attriblist) + public static ContextHandle CreateContext(IntPtr device, int[] attriblist) { unsafe { @@ -114,38 +114,38 @@ namespace OpenTK.Audio /// A pointer to the new context. /// Returns True on success, or False on failure. [DllImport(Alc.Lib, EntryPoint = "alcMakeContextCurrent", ExactSpelling = true, CallingConvention = Alc.Style), SuppressUnmanagedCodeSecurity()] - public static extern bool MakeContextCurrent([In] IntPtr context); + public static extern bool MakeContextCurrent([In] ContextHandle context); // ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent( ALCcontext *context ); /// This function tells a context to begin processing. When a context is suspended, changes in OpenAL state will be accepted but will not be processed. alcSuspendContext can be used to suspend a context, and then all the OpenAL state changes can be applied at once, followed by a call to alcProcessContext to apply all the state changes immediately. In some cases, this procedure may be more efficient than application of properties in a non-suspended state. In some implementations, process and suspend calls are each a NOP. /// a pointer to the new context [DllImport(Alc.Lib, EntryPoint = "alcProcessContext", ExactSpelling = true, CallingConvention = Alc.Style), SuppressUnmanagedCodeSecurity()] - public static extern void ProcessContext([In] IntPtr context); + public static extern void ProcessContext([In] ContextHandle context); // ALC_API void ALC_APIENTRY alcProcessContext( ALCcontext *context ); /// This function suspends processing on a specified context. When a context is suspended, changes in OpenAL state will be accepted but will not be processed. A typical use of alcSuspendContext would be to suspend a context, apply all the OpenAL state changes at once, and then call alcProcessContext to apply all the state changes at once. In some cases, this procedure may be more efficient than application of properties in a non-suspended state. In some implementations, process and suspend calls are each a NOP. /// a pointer to the context to be suspended. [DllImport(Alc.Lib, EntryPoint = "alcSuspendContext", ExactSpelling = true, CallingConvention = Alc.Style), SuppressUnmanagedCodeSecurity()] - public static extern void SuspendContext([In] IntPtr context); + public static extern void SuspendContext([In] ContextHandle context); // ALC_API void ALC_APIENTRY alcSuspendContext( ALCcontext *context ); /// This function destroys a context. /// a pointer to the new context. [DllImport(Alc.Lib, EntryPoint = "alcDestroyContext", ExactSpelling = true, CallingConvention = Alc.Style), SuppressUnmanagedCodeSecurity()] - public static extern void DestroyContext([In] IntPtr context); + public static extern void DestroyContext([In] ContextHandle context); // ALC_API void ALC_APIENTRY alcDestroyContext( ALCcontext *context ); /// This function retrieves the current context. /// Returns a pointer to the current context. [DllImport(Alc.Lib, EntryPoint = "alcGetCurrentContext", ExactSpelling = true, CallingConvention = Alc.Style), SuppressUnmanagedCodeSecurity()] - public static extern IntPtr GetCurrentContext(); + public static extern ContextHandle GetCurrentContext(); // ALC_API ALCcontext * ALC_APIENTRY alcGetCurrentContext( void ); /// This function retrieves a context's device pointer. /// a pointer to a context. /// Returns a pointer to the specified context's device. [DllImport(Alc.Lib, EntryPoint = "alcGetContextsDevice", ExactSpelling = true, CallingConvention = Alc.Style), SuppressUnmanagedCodeSecurity()] - public static extern IntPtr GetContextsDevice([In] IntPtr context); + public static extern IntPtr GetContextsDevice([In] ContextHandle context); // ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice( ALCcontext *context ); #endregion Context Management diff --git a/Source/OpenTK/ContextHandle.cs b/Source/OpenTK/ContextHandle.cs index 02d621e6..37e77f3a 100644 --- a/Source/OpenTK/ContextHandle.cs +++ b/Source/OpenTK/ContextHandle.cs @@ -12,16 +12,147 @@ using System.Text; namespace OpenTK { - public class ContextHandle : /*System.Runtime.InteropServices.SafeHandle,*/ IComparable + /// + /// Represents a handle to an OpenGL or OpenAL context. + /// + public struct ContextHandle : IComparable, IEquatable { + #region Fields + IntPtr handle; + + /// + /// Gets a System.IntPtr that represents the handle of this ContextHandle. + /// public IntPtr Handle { get { return handle; } } - public ContextHandle() /*: base(IntPtr.Zero, true)*/ { } + /// A read-only field that represents a handle that has been initialized to zero. + public static readonly ContextHandle Zero = new ContextHandle(IntPtr.Zero); + + #endregion + + #region Constructors + + /// + /// Constructs a new instance with the specified handle. + /// + /// A System.IntPtr containing the value for this instance. public ContextHandle(IntPtr h) { handle = h; } + #endregion + + #region Public Members + + #region ToString + + /// + /// Converts this instance to its equivalent string representation. + /// + /// A System.String that contains the string representation of this instance. + public override string ToString() + { + return Handle.ToString(); + } + + #endregion + + #region Equals + + /// + /// Compares this instance to the specified object. + /// + /// The System.Object to compare to. + /// True if obj is a ContextHandle that is equal to this instance; false otherwise. + public override bool Equals(object obj) + { + if (obj is ContextHandle) + return this.Equals((ContextHandle)obj); + return false; + } + + #endregion + + #region GetHashCode + + /// + /// Returns the hash code for this instance. + /// + /// A System.Int32 with the hash code of this instance. + public override int GetHashCode() + { + return Handle.GetHashCode(); + } + + #endregion + + #region public static explicit operator IntPtr(ContextHandle c) + + /// + /// Converts the specified ContextHandle to the equivalent IntPtr. + /// + /// The ContextHandle to convert. + /// A System.IntPtr equivalent to the specified ContextHandle. + public static explicit operator IntPtr(ContextHandle c) + { + return c != ContextHandle.Zero ? c.handle : IntPtr.Zero; + } + + #endregion + + #region public static explicit operator ContextHandle(IntPtr p) + + /// + /// Converts the specified IntPtr to the equivalent ContextHandle. + /// + /// The System.IntPtr to convert. + /// A ContextHandle equivalent to the specified IntPtr. + public static explicit operator ContextHandle(IntPtr p) + { + return new ContextHandle(p); + } + + #endregion + + #region public static bool operator ==(ContextHandle left, ContextHandle right) + + /// + /// Compares two ContextHandles for equality. + /// + /// The ContextHandle to compare. + /// The ContextHandle to compare to. + /// True if left is equal to right; false otherwise. + public static bool operator ==(ContextHandle left, ContextHandle right) + { + return left.Equals(right); + } + + #endregion + + #region public static bool operator !=(ContextHandle left, ContextHandle right) + + /// + /// Compares two ContextHandles for inequality. + /// + /// The ContextHandle to compare. + /// The ContextHandle to compare to. + /// True if left is not equal to right; false otherwise. + public static bool operator !=(ContextHandle left, ContextHandle right) + { + return !left.Equals(right); + } + + #endregion + + #endregion + #region IComparable Members + /// + /// Compares the numerical value of this instance to the specified ContextHandle and + /// returns a value indicating their relative order. + /// + /// The ContextHandle to compare to. + /// Less than 0, if this instance is less than other; 0 if both are equal; Greater than 0 if other is greater than this instance. public int CompareTo(ContextHandle other) { unsafe { return (int)((int*)other.handle.ToPointer() - (int*)this.handle.ToPointer()); } @@ -29,46 +160,18 @@ namespace OpenTK #endregion - public override string ToString() + #region IEquatable Members + + /// + /// Compares this instance to the specified ContextHandle for equality. + /// + /// The ContextHandle to compare to. + /// True if this instance is equal to other; false otherwise. + public bool Equals(ContextHandle other) { - return Handle.ToString(); + return Handle == other.Handle; } - public override bool Equals(object obj) - { - if (obj is ContextHandle) - return this.Handle == ((ContextHandle)obj).Handle; - return false; - } - - public override int GetHashCode() - { - return Handle.GetHashCode(); - } - - /// A read-only field that represents a handle that has been initialized to zero. - public static readonly ContextHandle Zero = new ContextHandle(IntPtr.Zero); - - /* - public override bool IsInvalid - { - get { return handle == IntPtr.Zero; } - } - - protected override bool ReleaseHandle() - { - throw new NotImplementedException(); - } - */ - - public static implicit operator IntPtr(ContextHandle c) - { - return c != null ? c.handle : IntPtr.Zero; - } - - public static implicit operator ContextHandle(IntPtr p) - { - return new ContextHandle(p); - } + #endregion } } diff --git a/Source/OpenTK/Platform/DummyGLContext.cs b/Source/OpenTK/Platform/DummyGLContext.cs index 7edadadb..64d8e9bb 100644 --- a/Source/OpenTK/Platform/DummyGLContext.cs +++ b/Source/OpenTK/Platform/DummyGLContext.cs @@ -37,7 +37,7 @@ namespace OpenTK.Platform public void CreateContext(bool direct, IGraphicsContext source) { - if (handle == null) + if (handle == ContextHandle.Zero) { ++handle_count; handle = new ContextHandle((IntPtr)handle_count); diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index de2cb30e..c08cda77 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -75,9 +75,9 @@ namespace OpenTK.Platform.Windows // and Wgl extensions will fail to load. Debug.Write("Creating render context... "); renderContext = new ContextHandle(Wgl.Imports.CreateContext(currentWindow.DeviceContext)); - if (renderContext == IntPtr.Zero) + if (renderContext == ContextHandle.Zero) renderContext = new ContextHandle(Wgl.Imports.CreateContext(currentWindow.DeviceContext)); - if (renderContext == IntPtr.Zero) + if (renderContext == ContextHandle.Zero) throw new GraphicsContextException(String.Format("Context creation failed. Wgl.CreateContext() error: {0}.", Marshal.GetLastWin32Error())); @@ -86,7 +86,7 @@ namespace OpenTK.Platform.Windows if (sharedContext != null) { Debug.Print("Sharing state with context {0}", sharedContext.ToString()); - Wgl.Imports.ShareLists((sharedContext as IGraphicsContextInternal).Context, renderContext); + Wgl.Imports.ShareLists((sharedContext as IGraphicsContextInternal).Context.Handle, renderContext.Handle); } } @@ -94,8 +94,8 @@ namespace OpenTK.Platform.Windows { if (window == null) throw new ArgumentNullException("window"); - renderContext = Wgl.GetCurrentContext(); - if (renderContext == IntPtr.Zero) + renderContext = new ContextHandle(Wgl.GetCurrentContext()); + if (renderContext == ContextHandle.Zero) throw new InvalidOperationException("No OpenGL context is current in the calling thread."); currentWindow = (WinWindowInfo)window; @@ -156,7 +156,7 @@ namespace OpenTK.Platform.Windows throw new ArgumentException("window", "Must point to a valid window."); currentWindow = (WinWindowInfo)window; - success = Wgl.Imports.MakeCurrent(((WinWindowInfo)window).DeviceContext, this.renderContext); + success = Wgl.Imports.MakeCurrent(((WinWindowInfo)window).DeviceContext, this.renderContext.Handle); } else success = Wgl.Imports.MakeCurrent(IntPtr.Zero, IntPtr.Zero); @@ -173,7 +173,7 @@ namespace OpenTK.Platform.Windows public bool IsCurrent { - get { return Wgl.GetCurrentContext() == this.renderContext; } + get { return Wgl.GetCurrentContext() == this.renderContext.Handle; } /* set { @@ -336,7 +336,7 @@ namespace OpenTK.Platform.Windows static ContextHandle GetCurrentContext() { - return Wgl.GetCurrentContext(); + return new ContextHandle(Wgl.GetCurrentContext()); } #endregion @@ -388,7 +388,7 @@ namespace OpenTK.Platform.Windows if (Destroy != null) Destroy(this, EventArgs.Empty); - if (renderContext != IntPtr.Zero) + if (renderContext != ContextHandle.Zero) { try { @@ -396,7 +396,7 @@ namespace OpenTK.Platform.Windows Debug.Print("Failed to make OpenGL context {0} non current. Error: {1}", renderContext.ToString(), Marshal.GetLastWin32Error()); - if (!Wgl.Imports.DeleteContext(renderContext)) + if (!Wgl.Imports.DeleteContext(renderContext.Handle)) Debug.Print("Failed to destroy OpenGL context {0}. Error: {1}", renderContext.ToString(), Marshal.GetLastWin32Error()); } @@ -408,7 +408,7 @@ namespace OpenTK.Platform.Windows Debug.WriteLine(e.ToString()); Debug.Unindent(); } - renderContext = null; + renderContext = ContextHandle.Zero; } //if (deviceContext != IntPtr.Zero) diff --git a/Source/OpenTK/Platform/X11/Bindings/Glx.cs b/Source/OpenTK/Platform/X11/Bindings/Glx.cs index 4b36afb2..769615cc 100644 --- a/Source/OpenTK/Platform/X11/Bindings/Glx.cs +++ b/Source/OpenTK/Platform/X11/Bindings/Glx.cs @@ -283,12 +283,18 @@ namespace OpenTK.Platform.X11 [DllImport(Library, EntryPoint = "glXDestroyContext")] public static extern void DestroyContext(IntPtr dpy, IntPtr context); + [DllImport(Library, EntryPoint = "glXDestroyContext")] + public static extern void DestroyContext(IntPtr dpy, ContextHandle context); + [DllImport(Library, EntryPoint = "glXGetCurrentContext")] public static extern IntPtr GetCurrentContext(); [DllImport(Library, EntryPoint = "glXMakeCurrent")] public static extern bool MakeCurrent(IntPtr display, IntPtr drawable, IntPtr context); + [DllImport(Library, EntryPoint = "glXMakeCurrent")] + public static extern bool MakeCurrent(IntPtr display, IntPtr drawable, ContextHandle context); + [DllImport(Library, EntryPoint = "glXSwapBuffers")] public static extern void SwapBuffers(IntPtr display, IntPtr drawable); diff --git a/Source/OpenTK/Platform/X11/X11GLContext.cs b/Source/OpenTK/Platform/X11/X11GLContext.cs index 8873e98f..db72dc26 100644 --- a/Source/OpenTK/Platform/X11/X11GLContext.cs +++ b/Source/OpenTK/Platform/X11/X11GLContext.cs @@ -21,7 +21,7 @@ namespace OpenTK.Platform.X11 /// internal sealed class X11GLContext : IGraphicsContext, IGraphicsContextInternal { - IntPtr context; + ContextHandle context; //X11WindowInfo window; X11WindowInfo currentWindow; //IntPtr visual; @@ -108,10 +108,10 @@ namespace OpenTK.Platform.X11 lock (API.Lock) { XVisualInfo info = window.VisualInfo; // Cannot pass a Property by reference. - context = Glx.CreateContext(window.Display, ref info, shareHandle.Handle, direct); + context = new ContextHandle(Glx.CreateContext(window.Display, ref info, shareHandle.Handle, direct)); // Context creation succeeded, return. - if (context != IntPtr.Zero) + if (context != ContextHandle.Zero) { Debug.Print("done! (id: {0})", context); return; @@ -120,8 +120,8 @@ namespace OpenTK.Platform.X11 // Context creation failed. Retry with a non-shared context with the direct/indirect bit flipped. Debug.Print("failed."); Debug.Write(String.Format("Creating OpenGL context: {0}, not shared... ", !direct ? "direct" : "indirect")); - context = Glx.CreateContext(window.Display, ref info, IntPtr.Zero, !direct); - if (context != IntPtr.Zero) + context = new ContextHandle(Glx.CreateContext(window.Display, ref info, IntPtr.Zero, !direct)); + if (context != ContextHandle.Zero) { Debug.Print("done! (id: {0})", context); return; @@ -173,7 +173,7 @@ namespace OpenTK.Platform.X11 Debug.Write(String.Format("Making context {0} current on thread {1} (Display: {2}, Screen: {3}, Window: {4})... ", context, System.Threading.Thread.CurrentThread.ManagedThreadId, w.Display, w.Screen, w.WindowHandle)); - if (w.Display == IntPtr.Zero || w.WindowHandle == IntPtr.Zero || context == IntPtr.Zero) + if (w.Display == IntPtr.Zero || w.WindowHandle == IntPtr.Zero || context == ContextHandle.Zero) throw new InvalidOperationException("Invalid display, window or context."); result = Glx.MakeCurrent(w.Display, w.WindowHandle, context); @@ -190,7 +190,7 @@ namespace OpenTK.Platform.X11 public bool IsCurrent { - get { return Glx.GetCurrentContext() == this.context; } + get { return Glx.GetCurrentContext() == this.context.Handle; } //set //{ // if (value) @@ -271,7 +271,7 @@ namespace OpenTK.Platform.X11 #endregion - #region IntPtr IGLContextInternal.Context + #region ContextHandle IGLContextInternal.Context ContextHandle IGraphicsContextInternal.Context {