Subversion Repositories AndroidProjects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1452 chris 1
#region --- License ---
2
/* Licensed under the MIT/X11 license.
3
 * Copyright (c) 2006-2008 the OpenTK Team.
4
 * This notice may not be removed from any source distribution.
5
 * See license.txt for licensing detailed licensing details.
6
 */
7
#endregion
8
 
9
using System;
10
using System.Collections.Generic;
11
using System.Text;
12
using System.Diagnostics;
13
 
14
using OpenTK.Platform;
15
 
16
namespace OpenTK.Graphics
17
{
18
    /// <summary>
19
    /// Represents and provides methods to manipulate an OpenGL render context.
20
    /// </summary>
21
    public sealed class GraphicsContext : IGraphicsContext, IGraphicsContextInternal
22
    {
23
        #region --- Fields ---
24
 
25
        IGraphicsContext implementation;  // The actual render context implementation for the underlying platform.
26
        bool disposed;
27
        // Indicates that this context was created through external means, e.g. Tao.Sdl or GLWidget#.
28
        // In this case, We'll assume that the external program will manage the lifetime of this
29
        // context - we'll not destroy it manually.
30
        readonly bool IsExternal;
31
        bool check_errors = true;
32
 
33
        static bool share_contexts = true;
34
        static bool direct_rendering = true;
35
        readonly static object SyncRoot = new object();        
36
        // Maps OS-specific context handles to GraphicsContext weak references.
37
        readonly static Dictionary<ContextHandle, WeakReference> available_contexts = new Dictionary<ContextHandle, WeakReference>();
38
 
39
        #endregion
40
 
41
        #region --- Constructors ---
42
 
43
        // Necessary to allow creation of dummy GraphicsContexts (see CreateDummyContext static method).
44
        GraphicsContext(ContextHandle handle)
45
        {
46
            implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle);
47
 
48
            lock (SyncRoot)
49
            {
50
                available_contexts.Add((implementation as IGraphicsContextInternal).Context, new WeakReference(this));
51
            }
52
        }
53
 
54
        /// <summary>
55
        /// Constructs a new GraphicsContext with the specified GraphicsMode and attaches it to the specified window.
56
        /// </summary>
57
        /// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GraphicsContext.</param>
58
        /// <param name="window">The OpenTK.Platform.IWindowInfo to attach the GraphicsContext to.</param>
59
        public GraphicsContext(GraphicsMode mode, IWindowInfo window)
60
            : this(mode, window, 1, 0, GraphicsContextFlags.Default)
61
        { }
62
 
63
        /// <summary>
64
        /// Constructs a new GraphicsContext with the specified GraphicsMode, version and flags,  and attaches it to the specified window.
65
        /// </summary>
66
        /// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GraphicsContext.</param>
67
        /// <param name="window">The OpenTK.Platform.IWindowInfo to attach the GraphicsContext to.</param>
68
        /// <param name="major">The major version of the new GraphicsContext.</param>
69
        /// <param name="minor">The minor version of the new GraphicsContext.</param>
70
        /// <param name="flags">The GraphicsContextFlags for the GraphicsContext.</param>
71
        /// <remarks>
72
        /// Different hardware supports different flags, major and minor versions. Invalid parameters will be silently ignored.
73
        /// </remarks>
74
        public GraphicsContext(GraphicsMode mode, IWindowInfo window, int major, int minor, GraphicsContextFlags flags)
75
        {
76
            lock (SyncRoot)
77
            {
78
                bool designMode = false;
79
                if (mode == null && window == null)
80
                    designMode = true;
81
                else if (mode == null) throw new ArgumentNullException("mode", "Must be a valid GraphicsMode.");
82
                else if (window == null) throw new ArgumentNullException("window", "Must point to a valid window.");
83
 
84
                // Silently ignore invalid major and minor versions.
85
                if (major <= 0)
86
                    major = 1;
87
                if (minor < 0)
88
                    minor = 0;
89
 
90
                Debug.Print("Creating GraphicsContext.");
91
                try
92
                {
93
                    Debug.Indent();
94
                    Debug.Print("GraphicsMode: {0}", mode);
95
                    Debug.Print("IWindowInfo: {0}", window);
96
                    Debug.Print("GraphicsContextFlags: {0}", flags);
97
                    Debug.Print("Requested version: {0}.{1}", major, minor);
98
 
99
                    IGraphicsContext shareContext = shareContext = FindSharedContext();
100
 
101
                    // Todo: Add a DummyFactory implementing IPlatformFactory.
102
                    if (designMode)
103
                    {
104
                        implementation = new Platform.Dummy.DummyGLContext();
105
                    }
106
                    else
107
                    {
108
                        IPlatformFactory factory = null;
109
                        switch ((flags & GraphicsContextFlags.Embedded) == GraphicsContextFlags.Embedded)
110
                        {
111
                            case false: factory = Factory.Default; break;
112
                            case true: factory = Factory.Embedded; break;
113
                        }
114
 
115
                        implementation = factory.CreateGLContext(mode, window, shareContext, direct_rendering, major, minor, flags);
116
                        // Note: this approach does not allow us to mix native and EGL contexts in the same process.
117
                        // This should not be a problem, as this use-case is not interesting for regular applications.
118
                        // Note 2: some platforms may not support a direct way of getting the current context
119
                        // (this happens e.g. with DummyGLContext). In that case, we use a slow fallback which
120
                        // iterates through all known contexts and checks if any is current (check GetCurrentContext
121
                        // declaration).
122
                        if (GetCurrentContext == null)
123
                        {
124
                            GetCurrentContextDelegate temp = factory.CreateGetCurrentGraphicsContext();
125
                            if (temp != null)
126
                            {
127
                                GetCurrentContext = temp;
128
                            }
129
                        }
130
                    }
131
 
132
                    available_contexts.Add((this as IGraphicsContextInternal).Context, new WeakReference(this));
133
                }
134
                finally
135
                {
136
                    Debug.Unindent();
137
                }
138
            }
139
        }
140
 
141
        /// <summary>
142
        /// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK.
143
        /// </summary>
144
        /// <param name="handle">The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK.</param>
145
        /// <param name="window">The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo.</param>
146
        /// <exception cref="GraphicsContextException">Occurs if handle is identical to a context already registered with OpenTK.</exception>
147
        public GraphicsContext(ContextHandle handle, IWindowInfo window)
148
            : this(handle, window, null, 1, 0, GraphicsContextFlags.Default)
149
        { }
150
 
151
        /// <summary>
152
        /// Constructs a new GraphicsContext from a pre-existing context created outside of OpenTK.
153
        /// </summary>
154
        /// <param name="handle">The handle of the existing context. This must be a valid, unique handle that is not known to OpenTK.</param>
155
        /// <param name="window">The window this context is bound to. This must be a valid window obtained through Utilities.CreateWindowInfo.</param>
156
        /// <param name="shareContext">A different context that shares resources with this instance, if any.
157
        /// Pass null if the context is not shared or if this is the first GraphicsContext instruct you construct.</param>
158
        /// <param name="major">The major version of the context (e.g. "2" for "2.1").</param>
159
        /// <param name="minor">The minor version of the context (e.g. "1" for "2.1").</param>
160
        /// <param name="flags">A bitwise combination of <see cref="GraphicsContextFlags"/> that describe this context.</param>
161
        /// <exception cref="GraphicsContextException">Occurs if handle is identical to a context already registered with OpenTK.</exception>
162
        public GraphicsContext(ContextHandle handle, IWindowInfo window, IGraphicsContext shareContext, int major, int minor, GraphicsContextFlags flags)
163
        {
164
            lock (SyncRoot)
165
            {
166
                IsExternal = true;
167
 
168
                if (handle == ContextHandle.Zero)
169
                {
170
                    implementation = new OpenTK.Platform.Dummy.DummyGLContext(handle);
171
                }
172
                else if (available_contexts.ContainsKey(handle))
173
                {
174
                    throw new GraphicsContextException("Context already exists.");
175
                }
176
                else
177
                {
178
                    switch ((flags & GraphicsContextFlags.Embedded) == GraphicsContextFlags.Embedded)
179
                    {
180
                        case false: implementation = Factory.Default.CreateGLContext(handle, window, shareContext, direct_rendering, major, minor, flags); break;
181
                        case true: implementation = Factory.Embedded.CreateGLContext(handle, window, shareContext, direct_rendering, major, minor, flags); break;
182
                    }
183
                }
184
 
185
                available_contexts.Add((implementation as IGraphicsContextInternal).Context, new WeakReference(this));
186
 
187
                (this as IGraphicsContextInternal).LoadAll();
188
            }
189
        }
190
 
191
        #endregion
192
 
193
        #region Private Members
194
 
195
        static IGraphicsContext FindSharedContext()
196
        {
197
            if (GraphicsContext.ShareContexts)
198
            {
199
                // A small hack to create a shared context with the first available context.
200
                foreach (WeakReference r in GraphicsContext.available_contexts.Values)
201
                {
202
                    // Fix for bug 1874: if a GraphicsContext gets finalized
203
                    // (but not disposed), it won't be removed from available_contexts
204
                    // making this return null even if another valid context exists.
205
                    // The workaround is to simply ignore null targets.
206
                    IGraphicsContext target = r.Target as IGraphicsContext;
207
                    if (target != null)
208
                        return target;
209
                }
210
            }
211
            return null;
212
        }
213
 
214
        #endregion
215
 
216
        #region --- Static Members ---
217
 
218
        #region public static GraphicsContext CreateDummyContext()
219
 
220
        /// <summary>
221
        /// Creates a dummy GraphicsContext to allow OpenTK to work with contexts created by external libraries.
222
        /// </summary>
223
        /// <returns>A new, dummy GraphicsContext instance.</returns>
224
        /// <remarks>
225
        /// <para>Instances created by this method will not be functional. Instance methods will have no effect.</para>
226
        /// <para>This method requires that a context is current on the calling thread.</para>
227
        /// </remarks>
228
        public static GraphicsContext CreateDummyContext()
229
        {
230
            ContextHandle handle = GetCurrentContext();
231
            if (handle == ContextHandle.Zero)
232
                throw new InvalidOperationException("No GraphicsContext is current on the calling thread.");
233
 
234
            return CreateDummyContext(handle);
235
        }
236
 
237
        /// <summary>
238
        /// Creates a dummy GraphicsContext to allow OpenTK to work with contexts created by external libraries.
239
        /// </summary>
240
        /// <param name="handle">The handle of a context.</param>
241
        /// <returns>A new, dummy GraphicsContext instance.</returns>
242
        /// <remarks>
243
        /// <para>Instances created by this method will not be functional. Instance methods will have no effect.</para>
244
        /// </remarks>
245
        public static GraphicsContext CreateDummyContext(ContextHandle handle)
246
        {
247
            if (handle == ContextHandle.Zero)
248
                throw new ArgumentOutOfRangeException("handle");
249
 
250
            return new GraphicsContext(handle);
251
        }
252
 
253
        #endregion
254
 
255
        #region public static void Assert()
256
 
257
        /// <summary>
258
        /// Checks if a GraphicsContext exists in the calling thread and throws a GraphicsContextMissingException if it doesn't.
259
        /// </summary>
260
        /// <exception cref="GraphicsContextMissingException">Generated when no GraphicsContext is current in the calling thread.</exception>
261
        public static void Assert()
262
        {
263
            if (GraphicsContext.CurrentContext == null)
264
                throw new GraphicsContextMissingException();
265
        }
266
 
267
        #endregion
268
 
269
        #region public static IGraphicsContext CurrentContext
270
 
271
        internal delegate ContextHandle GetCurrentContextDelegate();
272
        internal static GetCurrentContextDelegate GetCurrentContext = delegate
273
        {
274
            // Note: this is a slow, generic fallback for use with DummyGLContext.
275
            // Most other platforms can query the current context directly (via
276
            // [Wgl|Glx|Agl|Egl].GetCurrentContext()) so the GraphicsContext
277
            // constructor will replace this implementation with a platform-specific
278
            // one, if it exists.
279
            foreach (WeakReference weak_ref in available_contexts.Values)
280
            {
281
                IGraphicsContext context = (IGraphicsContext)weak_ref.Target;
282
                if (context.IsCurrent)
283
                {
284
                    return (context as IGraphicsContextInternal).Context;
285
                }
286
            }
287
 
288
            return ContextHandle.Zero;
289
        };
290
 
291
        /// <summary>
292
        /// Gets the GraphicsContext that is current in the calling thread.
293
        /// </summary>
294
        /// <remarks>
295
        /// Note: this property will not function correctly when both desktop and EGL contexts are
296
        /// available in the same process. This scenario is very unlikely to appear in practice.
297
        /// </remarks>
298
        public static IGraphicsContext CurrentContext
299
        {
300
            get
301
            {
302
                lock (SyncRoot)
303
                {
304
                    if (available_contexts.Count > 0)
305
                    {
306
                        ContextHandle handle = GetCurrentContext();
307
                        if (handle.Handle != IntPtr.Zero)
308
                            return (GraphicsContext)available_contexts[handle].Target;
309
                    }
310
                    return null;
311
                }
312
            }
313
        }
314
 
315
        #endregion
316
 
317
        #region public static bool ShareContexts
318
 
319
        /// <summary>Gets or sets a System.Boolean, indicating whether GraphicsContext resources are shared</summary>
320
        /// <remarks>
321
        /// <para>If ShareContexts is true, new GLContexts will share resources. If this value is
322
        /// false, new GLContexts will not share resources.</para>
323
        /// <para>Changing this value will not affect already created GLContexts.</para>
324
        /// </remarks>
325
        public static bool ShareContexts { get { return share_contexts; } set { share_contexts = value; } }
326
 
327
        #endregion
328
 
329
        #region public static bool DirectRendering
330
 
331
        /// <summary>Gets or sets a System.Boolean, indicating whether GraphicsContexts will perform direct rendering.</summary>
332
        /// <remarks>
333
        /// <para>
334
        /// If DirectRendering is true, new contexts will be constructed with direct rendering capabilities, if possible.
335
        /// If DirectRendering is false, new contexts will be constructed with indirect rendering capabilities.
336
        /// </para>
337
        /// <para>This property does not affect existing GraphicsContexts, unless they are recreated.</para>
338
        /// <para>
339
        /// This property is ignored on Operating Systems without support for indirect rendering, like Windows and OS X.
340
        /// </para>
341
        /// </remarks>
342
        public static bool DirectRendering
343
        {
344
            get { return direct_rendering; }
345
            set { direct_rendering = value; }
346
        }
347
 
348
        #endregion
349
 
350
        #endregion
351
 
352
        #region --- IGraphicsContext Members ---
353
 
354
        /// <summary>
355
        /// Gets or sets a System.Boolean, indicating whether automatic error checking should be performed.
356
        /// Influences the debug version of OpenTK.dll, only.
357
        /// </summary>
358
        /// <remarks>Automatic error checking will clear the OpenGL error state. Set CheckErrors to false if you use
359
        /// the OpenGL error state in your code flow (e.g. for checking supported texture formats).</remarks>
360
        public bool ErrorChecking
361
        {
362
            get { return check_errors; }
363
            set { check_errors = value; }
364
        }
365
        /// <summary>
366
        /// Creates an OpenGL context with the specified direct/indirect rendering mode and sharing state with the
367
        /// specified IGraphicsContext.
368
        /// </summary>
369
        /// <param name="direct">Set to true for direct rendering or false otherwise.</param>
370
        /// <param name="source">The source IGraphicsContext to share state from.</param>.
371
        /// <remarks>
372
        /// <para>
373
        /// Direct rendering is the default rendering mode for OpenTK, since it can provide higher performance
374
        /// in some circumastances.
375
        /// </para>
376
        /// <para>
377
        /// The 'direct' parameter is a hint, and will ignored if the specified mode is not supported (e.g. setting
378
        /// indirect rendering on Windows platforms).
379
        /// </para>
380
        /// </remarks>
381
        void CreateContext(bool direct, IGraphicsContext source)
382
        {
383
            lock (SyncRoot)
384
            {
385
                available_contexts.Add((this as IGraphicsContextInternal).Context, new WeakReference(this));
386
            }
387
        }
388
 
389
        /// <summary>
390
        /// Swaps buffers on a context. This presents the rendered scene to the user.
391
        /// </summary>
392
        public void SwapBuffers()
393
        {
394
            implementation.SwapBuffers();
395
        }
396
 
397
        /// <summary>
398
        /// Makes the GraphicsContext the current rendering target.
399
        /// </summary>
400
        /// <param name="window">A valid <see cref="OpenTK.Platform.IWindowInfo" /> structure.</param>
401
        /// <remarks>
402
        /// You can use this method to bind the GraphicsContext to a different window than the one it was created from.
403
        /// </remarks>
404
        public void MakeCurrent(IWindowInfo window)
405
        {
406
            implementation.MakeCurrent(window);
407
        }
408
 
409
        /// <summary>
410
        /// Gets a <see cref="System.Boolean"/> indicating whether this instance is current in the calling thread.
411
        /// </summary>
412
        public bool IsCurrent
413
        {
414
            get { return implementation.IsCurrent; }
415
        }
416
 
417
        /// <summary>
418
        /// Gets a <see cref="System.Boolean"/> indicating whether this instance has been disposed.
419
        /// It is an error to access any instance methods if this property returns true.
420
        /// </summary>
421
        public bool IsDisposed
422
        {
423
            get { return disposed && implementation.IsDisposed; }
424
            private set { disposed = value; }
425
        }
426
 
427
        /// <summary>
428
        /// Gets or sets a value indicating whether VSync is enabled.
429
        /// </summary>
430
        public bool VSync
431
        {
432
            get { return implementation.VSync; }
433
            set { implementation.VSync = value;  }
434
        }
435
 
436
        /// <summary>
437
        /// Updates the graphics context.  This must be called when the render target
438
        /// is resized for proper behavior on Mac OS X.
439
        /// </summary>
440
        /// <param name="window"></param>
441
        public void Update(IWindowInfo window)
442
        {
443
            implementation.Update(window);
444
        }
445
 
446
        /// <summary>
447
        /// Loads all OpenGL entry points.
448
        /// </summary>
449
        /// <exception cref="OpenTK.Graphics.GraphicsContextException">
450
        /// Occurs when this instance is not current on the calling thread.
451
        /// </exception>
452
        public void LoadAll()
453
        {
454
            if (GraphicsContext.CurrentContext != this)
455
                throw new GraphicsContextException();
456
 
457
            implementation.LoadAll();
458
        }
459
 
460
        #endregion
461
 
462
        #region --- IGraphicsContextInternal Members ---
463
 
464
        /// <summary>
465
        /// Gets the platform-specific implementation of this IGraphicsContext.
466
        /// </summary>
467
        IGraphicsContext IGraphicsContextInternal.Implementation
468
        {
469
            get { return implementation; }
470
        }
471
 
472
        /// <summary>
473
        /// Gets a handle to the OpenGL rendering context.
474
        /// </summary>
475
        ContextHandle IGraphicsContextInternal.Context
476
        {
477
            get { return ((IGraphicsContextInternal)implementation).Context; }
478
        }
479
 
480
        /// <summary>
481
        /// Gets the GraphicsMode of the context.
482
        /// </summary>
483
        public GraphicsMode GraphicsMode
484
        {
485
            get { return (implementation as IGraphicsContext).GraphicsMode; }
486
        }
487
 
488
        /// <summary>
489
        /// Gets the address of an OpenGL extension function.
490
        /// </summary>
491
        /// <param name="function">The name of the OpenGL function (e.g. "glGetString")</param>
492
        /// <returns>
493
        /// A pointer to the specified function or IntPtr.Zero if the function isn't
494
        /// available in the current opengl context.
495
        /// </returns>
496
        IntPtr IGraphicsContextInternal.GetAddress(string function)
497
        {
498
            return (implementation as IGraphicsContextInternal).GetAddress(function);
499
        }
500
 
501
        #endregion
502
 
503
        #region --- IDisposable Members ---
504
 
505
        /// <summary>
506
        /// Disposes of the GraphicsContext.
507
        /// </summary>
508
        public void Dispose()
509
        {
510
            this.Dispose(true);
511
            GC.SuppressFinalize(this);
512
        }
513
 
514
        void Dispose(bool manual)
515
        {
516
            if (!IsDisposed)
517
            {
518
                Debug.Print("Disposing context {0}.", (this as IGraphicsContextInternal).Context.ToString());
519
                lock (SyncRoot)
520
                {
521
                    available_contexts.Remove((this as IGraphicsContextInternal).Context);
522
                }
523
 
524
                if (manual && !IsExternal)
525
                {
526
                    if (implementation != null)
527
                        implementation.Dispose();
528
                }
529
                IsDisposed = true;
530
            }
531
        }
532
 
533
        #endregion
534
    }
535
}