#region --- License --- /* Licensed under the MIT/X11 license. * Copyright (c) 2006-2008 the OpenTK Team. * This notice may not be removed from any source distribution. * See license.txt for licensing detailed licensing details. */ #endregion using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using OpenTK.Platform; namespace OpenTK.Graphics { /// /// Represents and provides methods to manipulate an OpenGL render context. /// public sealed class GraphicsContext : IGraphicsContext, IGraphicsContextInternal { #region --- Fields --- IGraphicsContext implementation; // The actual render context implementation for the underlying platform. List dispose_queue = new List(); bool disposed; // Indicates that this context was created through external means, e.g. Tao.Sdl or GLWidget#. // In this case, We'll assume that the external program will manage the lifetime of this // context - we'll not destroy it manually. //bool is_external; bool check_errors = true; static bool share_contexts = true; static bool direct_rendering = true; readonly static object context_lock = new object(); // Maps OS-specific context handles to GraphicsContext weak references. readonly static Dictionary available_contexts = new Dictionary(); #endregion #region --- Constructors --- static GraphicsContext() { GetCurrentContext = Factory.Default.CreateGetCurrentGraphicsContext(); } // Necessary to allow creation of dummy GraphicsContexts (see CreateDummyContext static method). GraphicsContext(ContextHandle handle) { implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle); lock (context_lock) { available_contexts.Add((implementation as IGraphicsContextInternal).Context, new WeakReference(this)); } } /// /// Constructs a new GraphicsContext with the specified GraphicsMode and attaches it to the specified window. /// /// The OpenTK.Graphics.GraphicsMode of the GraphicsContext. /// The OpenTK.Platform.IWindowInfo to attach the GraphicsContext to. public GraphicsContext(GraphicsMode mode, IWindowInfo window) : this(mode, window, 1, 0, GraphicsContextFlags.Default) { } /// /// Constructs a new GraphicsContext with the specified GraphicsMode, version and flags, and attaches it to the specified window. /// /// The OpenTK.Graphics.GraphicsMode of the GraphicsContext. /// The OpenTK.Platform.IWindowInfo to attach the GraphicsContext to. /// The major version of the new GraphicsContext. /// The minor version of the new GraphicsContext. /// The GraphicsContextFlags for the GraphicsContext. /// /// Different hardware supports different flags, major and minor versions. Invalid parameters will be silently ignored. /// public GraphicsContext(GraphicsMode mode, IWindowInfo window, int major, int minor, GraphicsContextFlags flags) { bool designMode = false; if (mode == null && window == null) designMode = true; else if (mode == null) throw new ArgumentNullException("mode", "Must be a valid GraphicsMode."); else if (window == null) throw new ArgumentNullException("window", "Must point to a valid window."); // Silently ignore invalid major and minor versions. if (major <= 0) major = 1; if (minor < 0) minor = 0; Debug.Print("Creating GraphicsContext."); try { Debug.Indent(); Debug.Print("GraphicsMode: {0}", mode); Debug.Print("IWindowInfo: {0}", window); IGraphicsContext shareContext = null; if (GraphicsContext.ShareContexts) { lock (context_lock) { // A small hack to create a shared context with the first available context. foreach (WeakReference r in GraphicsContext.available_contexts.Values) { shareContext = (IGraphicsContext)r.Target; break; } } } // Todo: Add a DummyFactory implementing IPlatformFactory. if (designMode) implementation = new Platform.Dummy.DummyGLContext(); else implementation = Factory.Default.CreateGLContext(mode, window, shareContext, DirectRendering, major, minor, flags); lock (context_lock) { available_contexts.Add((this as IGraphicsContextInternal).Context, new WeakReference(this)); } //(implementation as IGraphicsContextInternal).LoadAll(); } finally { Debug.Unindent(); } } #endregion #region --- Static Members --- #region public static GraphicsContext CreateDummyContext() /// /// Creates a dummy GraphicsContext to allow OpenTK to work with contexts created by external libraries. /// /// A new, dummy GraphicsContext instance. /// /// Instances created by this methodwill not be functional. Instance methods will have no effect. /// public static GraphicsContext CreateDummyContext() { ContextHandle handle = GetCurrentContext(); if (handle == ContextHandle.Zero) throw new InvalidOperationException("No GraphicsContext is current on the calling thread."); return CreateDummyContext(handle); } public static GraphicsContext CreateDummyContext(ContextHandle handle) { if (handle == ContextHandle.Zero) throw new ArgumentOutOfRangeException("handle"); return new GraphicsContext(handle); } #endregion #region public static void Assert() /// /// Checks if a GraphicsContext exists in the calling thread and throws a GraphicsContextException if it doesn't. /// /// Generated when no GraphicsContext is current in the calling thread. public static void Assert() { if (GraphicsContext.CurrentContext == null) throw new GraphicsContextMissingException(); } #endregion #region public static IGraphicsContext CurrentContext internal delegate ContextHandle GetCurrentContextDelegate(); internal static GetCurrentContextDelegate GetCurrentContext; /// /// Gets the GraphicsContext that is current in the calling thread. /// public static IGraphicsContext CurrentContext { get { lock (context_lock) { if (available_contexts.Count > 0) { ContextHandle handle = GetCurrentContext(); if (handle.Handle != IntPtr.Zero) return (GraphicsContext)available_contexts[handle].Target; } return null; } } } #endregion #region public static bool ShareContexts /// Gets or sets a System.Boolean, indicating whether GraphicsContext resources are shared /// /// If ShareContexts is true, new GLContexts will share resources. If this value is /// false, new GLContexts will not share resources. /// Changing this value will not affect already created GLContexts. /// public static bool ShareContexts { get { return share_contexts; } set { share_contexts = value; } } #endregion #region public static bool DirectRendering /// Gets or sets a System.Boolean, indicating whether GraphicsContexts will perform direct rendering. /// /// /// If DirectRendering is true, new contexts will be constructed with direct rendering capabilities, if possible. /// If DirectRendering is false, new contexts will be constructed with indirect rendering capabilities. /// /// This property does not affect existing GraphicsContexts, unless they are recreated. /// /// This property is ignored on Operating Systems without support for indirect rendering, like Windows and OS X. /// /// public static bool DirectRendering { get { return direct_rendering; } set { direct_rendering = value; } } #endregion #endregion #region --- Internal Members --- List error_list = new List(); // Retrieve all OpenGL errors to clear the error list. // See http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/geterror.html [Conditional("DEBUG")] internal void ResetErrors() { if (check_errors) { while (GL.GetError() != ErrorCode.NoError) { } } } // Retrieve all OpenGL errors and throw an exception if anything other than NoError is returned. [Conditional("DEBUG")] internal void CheckErrors() { if (check_errors) { error_list.Clear(); ErrorCode error; do { error = GL.GetError(); error_list.Add(error); } while (error != ErrorCode.NoError); if (error_list.Count != 1) { StringBuilder sb = new StringBuilder(); foreach (ErrorCode e in error_list) { if (e != ErrorCode.NoError) { sb.Append(e.ToString()); sb.Append(", "); } else break; } sb.Remove(sb.Length - 2, 2); // Remove the last comma throw new OpenGLErrorException(sb.ToString()); } } } #endregion #region --- Private Members --- #region void ContextDestroyed(IGraphicsContext context, EventArgs e) /// /// Handles the Destroy event. /// /// The OpenTK.Platform.IGraphicsContext that was destroyed. /// Not used. void ContextDestroyed(IGraphicsContext context, EventArgs e) { this.Destroy -= ContextDestroyed; //available_contexts.Remove(((IGraphicsContextInternal)this).Context); } #endregion #endregion #region --- IGraphicsContext Members --- /// /// Gets or sets a System.Boolean, indicating whether automatic error checking should be performed. /// Influences the debug version of OpenTK.dll, only. /// /// Automatic error checking will clear the OpenGL error state. Set CheckErrors to false if you use /// the OpenGL error state in your code flow (e.g. for checking supported texture formats). public bool ErrorChecking { get { return check_errors; } set { check_errors = value; } } /// /// Creates an OpenGL context with the specified direct/indirect rendering mode and sharing state with the /// specified IGraphicsContext. /// /// Set to true for direct rendering or false otherwise. /// The source IGraphicsContext to share state from.. /// /// /// Direct rendering is the default rendering mode for OpenTK, since it can provide higher performance /// in some circumastances. /// /// /// The 'direct' parameter is a hint, and will ignored if the specified mode is not supported (e.g. setting /// indirect rendering on Windows platforms). /// /// void CreateContext(bool direct, IGraphicsContext source) { this.Destroy += ContextDestroyed; lock (context_lock) { available_contexts.Add((this as IGraphicsContextInternal).Context, new WeakReference(this)); } //OpenTK.Graphics.OpenGL.GL.Clear(OpenTK.Graphics.OpenGL.ClearBufferMask.ColorBufferBit); //if (StaticGetCurrentContext == null) // StaticGetCurrentContext = implementation.GetCurrentContext; } /// /// Swaps buffers on a context. This presents the rendered scene to the user. /// public void SwapBuffers() { implementation.SwapBuffers(); } /// /// Makes the GraphicsContext the current rendering target. /// /// A valid structure. /// /// You can use this method to bind the GraphicsContext to a different window than the one it was created from. /// public void MakeCurrent(IWindowInfo window) { implementation.MakeCurrent(window); } /// /// Gets a System.Boolean indicating whether this Context is current in the calling thread. /// public bool IsCurrent { get { return implementation.IsCurrent; } //set { implementation.IsCurrent = value; } } /// /// Raised when a Context is destroyed. /// public event DestroyEvent Destroy { add { implementation.Destroy += value; } remove { implementation.Destroy -= value; } } /// /// Gets or sets a value indicating whether VSync is enabled. /// public bool VSync { get { return implementation.VSync; } set { implementation.VSync = value; } } /// /// Updates the graphics context. This must be called when the render target /// is resized for proper behavior on Mac OS X. /// /// public void Update(IWindowInfo window) { implementation.Update(window); } #endregion #region --- IGraphicsContextInternal Members --- /// /// Gets the platform-specific implementation of this IGraphicsContext. /// IGraphicsContext IGraphicsContextInternal.Implementation { get { return implementation; } } /// /// Loads all OpenGL extensions. /// void IGraphicsContextInternal.LoadAll() { (implementation as IGraphicsContextInternal).LoadAll(); } /// /// Gets a handle to the OpenGL rendering context. /// ContextHandle IGraphicsContextInternal.Context { get { return ((IGraphicsContextInternal)implementation).Context; } } /// /// Gets the GraphicsMode of the context. /// public GraphicsMode GraphicsMode { get { return (implementation as IGraphicsContext).GraphicsMode; } } /// /// Registers an OpenGL resource for disposal. /// /// The OpenGL resource to dispose. void IGraphicsContextInternal.RegisterForDisposal(IDisposable resource) { GC.KeepAlive(resource); dispose_queue.Add(resource); } /// /// Disposes all registered OpenGL resources. /// void IGraphicsContextInternal.DisposeResources() { foreach (IDisposable resource in dispose_queue) resource.Dispose(); dispose_queue.Clear(); } /// /// Gets the address of an OpenGL extension function. /// /// The name of the OpenGL function (e.g. "glGetString") /// /// A pointer to the specified function or IntPtr.Zero if the function isn't /// available in the current opengl context. /// IntPtr IGraphicsContextInternal.GetAddress(string function) { return (implementation as IGraphicsContextInternal).GetAddress(function); } #endregion #region --- IDisposable Members --- /// /// Disposes of the GraphicsContext. /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } void Dispose(bool manual) { if (!disposed) { Debug.Print("Disposing context {0}.", (this as IGraphicsContextInternal).Context.ToString()); lock (context_lock) { available_contexts.Remove((this as IGraphicsContextInternal).Context); } if (manual) { if (implementation != null) implementation.Dispose(); } disposed = true; } } //~GraphicsContext() //{ // this.Dispose(false); //} #endregion } #region public class GraphicsException : Exception /// Represents errors related to Graphics operations. public class GraphicsException : Exception { /// Constructs a new GraphicsException. public GraphicsException() : base() { } /// Constructs a new GraphicsException with the specified excpetion message. /// public GraphicsException(string message) : base(message) { } } #endregion #region class GraphicsErrorException : GraphicsException // This is private by design. These exceptions are only thrown in debug builds of // OpenTK and the user should *not* handle them. // If some specific OpenGL error is generated by design, the user should // turn off automatic error checking for that portion of the code, using // 'GraphicsContext.ErrorChecking = false'. class OpenGLErrorException : GraphicsException { public OpenGLErrorException(string message) : base(message) { } } #endregion }