From 50875eb42de39b6a604f34ec5a881aef4de9100f Mon Sep 17 00:00:00 2001 From: Nativo Labs Date: Sat, 14 Nov 2015 00:09:35 +0000 Subject: [PATCH 01/11] Added a Synchronization Context to rendering Thread. By doing so it is possible to use async / await patern in the rendering thread --- .../Platform/Android/AndroidGameView.cs | 130 +++- Source/OpenTK/Platform/Android/GLCalls.cs | 86 +-- .../Platform/Android/Threading/AsyncResult.cs | 220 ++++++ .../Android/Threading/BackgroundLooper.cs | 676 ++++++++++++++++++ .../Threading/DelegateQueueAsyncResult.cs | 66 ++ .../Android/Threading/IExecutionContext.cs | 37 + .../Rendering_ExecutionContext_Android.cs | 154 ++++ 7 files changed, 1321 insertions(+), 48 deletions(-) create mode 100755 Source/OpenTK/Platform/Android/Threading/AsyncResult.cs create mode 100755 Source/OpenTK/Platform/Android/Threading/BackgroundLooper.cs create mode 100755 Source/OpenTK/Platform/Android/Threading/DelegateQueueAsyncResult.cs create mode 100644 Source/OpenTK/Platform/Android/Threading/IExecutionContext.cs create mode 100644 Source/OpenTK/Platform/Android/Threading/Rendering_ExecutionContext_Android.cs diff --git a/Source/OpenTK/Platform/Android/AndroidGameView.cs b/Source/OpenTK/Platform/Android/AndroidGameView.cs index 1ea51e577..3ee91da9b 100644 --- a/Source/OpenTK/Platform/Android/AndroidGameView.cs +++ b/Source/OpenTK/Platform/Android/AndroidGameView.cs @@ -37,7 +37,7 @@ namespace OpenTK.Platform.Android #if OPENTK_0 [Register ("opentk/platform/android/AndroidGameView")] #else - [Register ("opentk_1_0/platform/android/AndroidGameView")] + [Register ("opentk_1_1/platform/android/AndroidGameView")] #endif public partial class AndroidGameView : GameViewBase, ISurfaceHolderCallback { @@ -58,6 +58,7 @@ public partial class AndroidGameView : GameViewBase, ISurfaceHolderCallback bool callMakeCurrent = false; CancellationTokenSource source; Task renderTask; + IExecutionContext renderingExecutionContext; Thread renderingThread; ManualResetEvent pauseSignal; global::Android.Graphics.Rect surfaceRect; @@ -71,18 +72,54 @@ public AndroidGameView (Context context) : base (context) Init (); } + public AndroidGameView (Context context, IExecutionContext executionContext) : base (context) + { + this.renderingExecutionContext = executionContext; + + if (executionContext != null) { + renderingThread = executionContext.Thread; + renderOnUIThread = false; + } + + Init (); + } [Register (".ctor", "(Landroid/content/Context;Landroid/util/AttributeSet;)V", "")] public AndroidGameView (Context context, IAttributeSet attrs) : base (context, attrs) { Init (); } + public AndroidGameView (Context context, IAttributeSet attrs, IExecutionContext executionContext) : base (context, attrs) + { + this.renderingExecutionContext = executionContext; + + if (executionContext != null) { + renderingThread = executionContext.Thread; + renderOnUIThread = false; + } + + Init (); + } + public AndroidGameView (IntPtr handle, global::Android.Runtime.JniHandleOwnership transfer) : base (handle, transfer) { Init (); } + public AndroidGameView (IntPtr handle, global::Android.Runtime.JniHandleOwnership transfer, IExecutionContext executionContext) + : base (handle, transfer) + { + this.renderingExecutionContext = executionContext; + + if (executionContext != null) { + renderingThread = executionContext.Thread; + renderOnUIThread = false; + } + + Init (); + } + private void Init () { // default @@ -245,6 +282,11 @@ public override void SwapBuffers () public override void Run () { EnsureUndisposed (); + + if (this.renderingExecutionContext != null) + throw new Exception ("When a rendering thread was specified it is not possible to start run-loop"); + + updates = 0; #if TIMING targetFps = currentFps = 0; @@ -256,7 +298,12 @@ public override void Run () public override void Run (double updatesPerSecond) { + EnsureUndisposed (); + + if (this.renderingExecutionContext != null) + throw new Exception ("When a rendering thread was specified it is not possible to start run-loop"); + #if TIMING avgFps = targetFps = currentFps = updatesPerSecond; #endif @@ -265,11 +312,34 @@ public override void Run (double updatesPerSecond) StartThread (); } + private void InitializeGraphicsContext_IfExectutionContextSet(){ + if (this.renderingExecutionContext == null) + return; + + if (renderingExecutionContext.State == ExecutionState.Paused) { + renderingExecutionContext.Resume (); + } + + renderingExecutionContext.Invoke (new Action (() => { + try { + MakeCurrent (); + } catch (Exception ex) { + OnRenderThreadExited(new EventArgs()); + } + }), null); + } + + public void Stop () { EnsureUndisposed (); - StopThread (); + if (renderingExecutionContext == null) { + StopThread (); + } else { + renderingExecutionContext.Stop (); + } + UnloadInternal (EventArgs.Empty); } @@ -277,14 +347,28 @@ public virtual void Pause () { log ("Pause"); EnsureUndisposed (); - PauseThread (); + + if (renderingExecutionContext == null) { + PauseThread (); + } + else{ + renderingExecutionContext.Pause (); + } } public virtual void Resume () { log ("Resume"); EnsureUndisposed (); - ResumeThread (); + + if (renderingExecutionContext == null) { + ResumeThread (); + } + else{ + renderingExecutionContext.Resume (); + } + + } #region Private @@ -615,7 +699,7 @@ public bool RenderOnUIThread { return renderOnUIThread; } set { - renderOnUIThread = value; + renderOnUIThread = value && this.renderingExecutionContext == null; } } @@ -738,6 +822,42 @@ public override Size Size { } } } + + public IExecutionContext RenderingExecutionContext{ + get{ + return renderingExecutionContext; + } + set{ + this.renderingExecutionContext = value; + + if (renderingExecutionContext != null) { + renderingThread = renderingExecutionContext.Thread; + renderOnUIThread = false; + } + } + } + + protected override void OnLoad (EventArgs e) + { + InitializeGraphicsContext_IfExectutionContextSet (); + + base.OnLoad (e); + } + + protected void RequestRenderFrame(){ + if (renderingExecutionContext == null) + throw new Exception ("This method can only be used when executionContext was specified"); + + global::Android.App.Application.SynchronizationContext.Send (_ => { + if (!disposed && renderingExecutionContext.State == ExecutionState.Executing) { + renderingExecutionContext.BeginInvoke (new Action (() => { + OnRenderFrame (new FrameEventArgs ()); + }), null); + } + }, null); + } + + #endregion } } diff --git a/Source/OpenTK/Platform/Android/GLCalls.cs b/Source/OpenTK/Platform/Android/GLCalls.cs index a41e1502b..d4cf9225f 100644 --- a/Source/OpenTK/Platform/Android/GLCalls.cs +++ b/Source/OpenTK/Platform/Android/GLCalls.cs @@ -18,17 +18,17 @@ using ES11 = OpenTK.Graphics.ES11; using ES20 = OpenTK.Graphics.ES20; using ES30 = OpenTK.Graphics.ES30; -using ES31 = OpenTK.Graphics.ES31; +//using ES31 = OpenTK.Graphics.ES31; namespace OpenTK { sealed class GLCalls { -#if OPENTK_0 + #if OPENTK_0 public GLContextVersion Version; -#else + #else public GLVersion Version; -#endif + #endif public delegate void glScissor (int x, int y, int width, int height); public delegate void glViewport (int x, int y, int width, int height); @@ -36,22 +36,22 @@ sealed class GLCalls public glScissor Scissor; public glViewport Viewport; -#if OPENTK_0 + #if OPENTK_0 public static GLCalls GetGLCalls (GLContextVersion api) { - switch (api) { - case GLContextVersion.Gles1_1: - return CreateES1 (); - case GLContextVersion.Gles2_0: - return CreateES2 (); - case GLContextVersion.Gles3_0: - return CreateES3 (); - case GLContextVersion.Gles3_1: - return CreateES31 (); - } - throw new ArgumentException ("api"); + switch (api) { + case GLContextVersion.Gles1_1: + return CreateES1 (); + case GLContextVersion.Gles2_0: + return CreateES2 (); + case GLContextVersion.Gles3_0: + return CreateES3 (); + case GLContextVersion.Gles3_1: + return CreateES31 (); } -#else + throw new ArgumentException ("api"); + } + #else public static GLCalls GetGLCalls (GLVersion api) { switch (api) { @@ -61,21 +61,21 @@ public static GLCalls GetGLCalls (GLVersion api) return CreateES2 (); case GLVersion.ES3: return CreateES3 (); - case GLVersion.ES31: - return CreateES31 (); + // case GLVersion.ES31: + // return CreateES31 (); } throw new ArgumentException ("api"); } -#endif + #endif public static GLCalls CreateES1 () { return new GLCalls () { -#if OPENTK_0 + #if OPENTK_0 Version = GLContextVersion.Gles1_1, -#else + #else Version = GLVersion.ES1, -#endif + #endif Scissor = (x, y, w, h) => ES11.GL.Scissor(x, y, w, h), Viewport = (x, y, w, h) => ES11.GL.Viewport(x, y, w, h), }; @@ -84,40 +84,40 @@ public static GLCalls CreateES1 () public static GLCalls CreateES2 () { return new GLCalls () { -#if OPENTK_0 + #if OPENTK_0 Version = GLContextVersion.Gles2_0, -#else + #else Version = GLVersion.ES2, -#endif + #endif Scissor = (x, y, w, h) => ES20.GL.Scissor(x, y, w, h), Viewport = (x, y, w, h) => ES20.GL.Viewport(x, y, w, h), - }; + }; } public static GLCalls CreateES3 () { return new GLCalls () { -#if OPENTK_0 + #if OPENTK_0 Version = GLContextVersion.Gles3_0, -#else + #else Version = GLVersion.ES3, -#endif + #endif Scissor = (x, y, w, h) => ES30.GL.Scissor(x, y, w, h), Viewport = (x, y, w, h) => ES30.GL.Viewport(x, y, w, h), - }; + }; } - public static GLCalls CreateES31 () - { - return new GLCalls () { -#if OPENTK_0 - Version = GLContextVersion.Gles3_1, -#else - Version = GLVersion.ES31, -#endif - Scissor = (x, y, w, h) => ES31.GL.Scissor(x, y, w, h), - Viewport = (x, y, w, h) => ES31.GL.Viewport(x, y, w, h), - }; - } + // public static GLCalls CreateES31 () + // { + // return new GLCalls () { + //#if OPENTK_0 + // Version = GLContextVersion.Gles3_1, + //#else + // Version = GLVersion.ES31, + //#endif + // Scissor = (x, y, w, h) => ES31.GL.Scissor(x, y, w, h), + // Viewport = (x, y, w, h) => ES31.GL.Viewport(x, y, w, h), + // }; + // } } } diff --git a/Source/OpenTK/Platform/Android/Threading/AsyncResult.cs b/Source/OpenTK/Platform/Android/Threading/AsyncResult.cs new file mode 100755 index 000000000..38953d897 --- /dev/null +++ b/Source/OpenTK/Platform/Android/Threading/AsyncResult.cs @@ -0,0 +1,220 @@ +#region License + +/* Copyright (c) 2006 Leslie Sanford + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#endregion + +#region Contact + +/* + * Leslie Sanford + * Email: jabberdabber@hotmail.com + */ + +#endregion + +using System; +using System.Threading; + +namespace OpenTK.Platform.Android.Threading +{ + /// + /// Provides basic implementation of the IAsyncResult interface. + /// + public class AsyncResult + : IAsyncResult + , IDisposable + { + #region AsyncResult Members + + #region Fields + + // A value indicating whether this AsyncResult object has been disposed. + private bool disposed; + + // The owner of this AsyncResult object. + private object owner; + + // The callback to be invoked when the operation completes. + private AsyncCallback callback; + + // User state information. + private object state; + + // For signaling when the operation has completed. + private ManualResetEvent waitHandle; + + // A value indicating whether the operation completed synchronously. + private bool completedSynchronously; + + // A value indicating whether the operation has completed. + private bool isCompleted; + + // The ID of the thread this AsyncResult object originated on. + private int threadId; + + #endregion + + #region Construction + + /// + /// Initializes a new instance of the AsyncResult object with the + /// specified owner of the AsyncResult object, the optional callback + /// delegate, and optional state object. + /// + /// + /// The owner of the AsyncResult object. + /// + /// + /// An optional asynchronous callback, to be called when the + /// operation is complete. + /// + /// + /// A user-provided object that distinguishes this particular + /// asynchronous request from other requests. + /// + public AsyncResult(object owner, AsyncCallback callback, object state) + { + this.owner = owner; + this.callback = callback; + this.state = state; + + // Get the current thread ID. This will be used later to determine + // if the operation completed synchronously. + this.threadId = Thread.CurrentThread.ManagedThreadId; + this.waitHandle = new ManualResetEvent(false); + } + + #endregion + + /// + /// The finalizer override method. + /// + ~AsyncResult() + { + Dispose(false); + } + + #region Methods + + /// + /// Signals that the operation has completed. + /// + public void Signal() + { + this.isCompleted = true; + this.completedSynchronously = threadId == Thread.CurrentThread.ManagedThreadId; + this.waitHandle.Set(); + + if (callback != null) + { + callback(this); + } + } + + #endregion + + #region Properties + + /// + /// Gets the owner of this AsyncResult object. + /// + public object Owner + { + get { return this.owner; } + } + + #endregion + + #endregion + + #region IAsyncResult Members + + /// + /// Gets a user-defined object that qualifies or contains information about an + /// asynchronous operation. + /// + public object AsyncState + { + get { return this.state; } + } + + /// + /// Gets a System.Threading.WaitHandle that is used to wait for an asynchronous + /// operation to complete. + /// + public WaitHandle AsyncWaitHandle + { + get { return this.waitHandle; } + } + + /// + /// Gets an indication of whether the asynchronous operation completed synchronously. + /// + public bool CompletedSynchronously + { + get { return this.completedSynchronously; } + } + + /// + /// Gets an indication whether the asynchronous operation has completed. + /// + public bool IsCompleted + { + get { return this.isCompleted; } + } + + #endregion + + /// + /// Performs application-defined tasks associated with freeing, releasing, or + /// resetting unmanaged resources. + /// + /// Indicates whether Dispose was called explicitly. + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + this.waitHandle.Close(); + } + + disposed = true; + } + } + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or + /// resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/OpenTK/Platform/Android/Threading/BackgroundLooper.cs b/Source/OpenTK/Platform/Android/Threading/BackgroundLooper.cs new file mode 100755 index 000000000..ecd649dd1 --- /dev/null +++ b/Source/OpenTK/Platform/Android/Threading/BackgroundLooper.cs @@ -0,0 +1,676 @@ +#region License + +/* Copyright (c) 2006 Leslie Sanford + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#endregion + +#region Contact + +/* + * Leslie Sanford + * Email: jabberdabber@hotmail.com + */ + +#endregion + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Reflection; +using System.Threading; + + +namespace OpenTK.Platform.Android.Threading +{ + /// + /// Represents an asynchronous queue of delegates. + /// + public class BackgroundLooper + : SynchronizationContext + , ISynchronizeInvoke + { + #region Constants + private const string OBJECT_DISPOSED_EXCEPTION = "Object already disposed"; + #endregion + + + #region DelegateQueue Members + + #region Fields + + // The thread for processing delegates. + private readonly Thread delegateThread; + + // The queue for holding delegates. + private readonly Queue delegateQueue; + + // The object to use for locking. + private readonly object lockObject; + + // Indicates whether the delegate queue has been disposed. + private volatile bool disposed; + + // Indicates whether the DelegateQueue is in the process of disposal. + private volatile bool disposing; + + // Thread ID counter for all DelegateQueues. + private static volatile uint threadID = 0; + + + private event EventHandler ExceptionThrownEvent; + private event EventHandler DisposedEvent; + + #endregion + + #region Events + + /// + /// Occurs when an exception is raised while executing some delegate + /// in the context of this DelegateQueue. + /// + public event EventHandler ExceptionThrown + { + add + { + if (disposed) + { + throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); + } + this.ExceptionThrownEvent += value; + } + remove { this.ExceptionThrownEvent -= value; } + } + + #endregion + + #region Construction + + /// + /// Initializes a new instance of the DelegateQueue class. + /// + public BackgroundLooper() + : this(0) + { + } + + /// + /// Initializes a new instance of the DelegateQueue class. + /// + /// + /// The maximum stack size to be used by the DelegateQueue + /// thread, or 0 to use the default maximum stack size specified in the + /// header for the executable. + /// + public BackgroundLooper(int maxStackSize) + { + // Create thread for processing delegates. + delegateThread = new Thread(DelegateProcedure, maxStackSize); + delegateThread.IsBackground = true; + delegateQueue = new Queue(); + lockObject = new object(); + } + + /// + /// The finalizer override method. + /// + ~BackgroundLooper() + { + Dispose(false); + } + + #endregion + + #region Properties + + /// + /// Gets a unique identifier for the managed thread + /// on which this DelegateQueue is + /// executing. + /// + public int ManagedThreadId + { + get { return this.delegateThread.ManagedThreadId; } + } + + /// + /// Gets a value indicating whether this DelegateQueue + /// is in the process of disposal. + /// + public bool Disposing + { + get { return this.disposing; } + } + + /// + /// Gets or sets the culture for the delegate queue thread. + /// + public CultureInfo CurrentCulture + { + get { return this.delegateThread.CurrentCulture; } + set { this.delegateThread.CurrentCulture = value; } + } + + /// + /// Gets or sets the current culture used by the Resource Manager to look up + /// culture-specific resources at run time. + /// + public CultureInfo CurrentUICulture + { + get { return this.delegateThread.CurrentUICulture; } + set { this.delegateThread.CurrentUICulture = value; } + } + + /// + /// Returns an System.Threading.ApartmentState value indicating the apartment state. + /// + /// + /// One of the System.Threading.ApartmentState values indicating the apartment + /// state of the delegate queue's managed thread. The default is + /// System.Threading.ApartmentState.Unknown. + /// + public ApartmentState GetApartmentState() + { + return this.delegateThread.GetApartmentState(); + } + + /// + /// Sets the apartment state of a delegate queue's thread before it is started. + /// + /// The new apartment state. + public void SetApartmentState(ApartmentState state) + { + this.delegateThread.SetApartmentState(state); + } + + /// + /// Sets the apartment state of a delegate queue's thread before it is started. + /// + /// The new apartment state. + /// True if the apartment state is set; otherwise, false. + public bool TrySetApartmentState(ApartmentState state) + { + return this.delegateThread.TrySetApartmentState(state); + } + + /// + /// Gets or sets a value indicating the scheduling priority of the + /// delegate queue's thread. + /// + public ThreadPriority Priority + { + get { return this.delegateThread.Priority; } + set { this.delegateThread.Priority = value; } + } + + #endregion + + #region Methods + + /// + /// Sets the preferred processor for the delegate queue's + /// system thread to run on. This method is executed + /// asynchronously as soon as possible. + /// + /// + /// The zero-based processor index specifying the delegate + /// queue's ideal processor. + /// + + + /// + /// Starts delegate execution in the delegate queue. + /// + public void Start() + { + if (disposed) + { + throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); + } + + if (delegateThread.IsAlive) + { + throw new InvalidOperationException(); + } + + lock (lockObject) + { + // Increment to next thread ID. + threadID++; + + // Create name for thread. + delegateThread.Name = "Delegate Queue Thread: Id[" + threadID + "] "; + + // Start thread. + delegateThread.Start(); + + Debug.WriteLine(delegateThread.Name + " Started."); + + // Wait for signal from thread that it is running. + Monitor.Wait(lockObject); + } + } + + /// + /// Pauses delegate execution in the delegate queue. + /// + public void Pause() + { + if (disposed) + { + throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); + } + + Monitor.Enter(lockObject); + } + + /// + /// Resumes delegate execution in the delegate queue. + /// + public void Resume() + { + if (disposed) + { + throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); + } + + Monitor.Exit(lockObject); + } + + // Processes and invokes delegates. + private void DelegateProcedure() + { + lock (lockObject) + { + // Signal the constructor that the thread is now running. + Monitor.Pulse(lockObject); + } + + // Set this DelegateQueue as the SynchronizationContext for this thread. + SetSynchronizationContext(this); + + // Placeholder for DelegateQueueAsyncResult objects. + DelegateQueueAsyncResult result = null; + + // While the DelegateQueue is not in the process + // of being disposed (i.e. disposing). + while (true) + { + // Critical section. + lock (lockObject) + { + if (disposing) + { + break; + } + + // If there are delegates waiting to be invoked. + if (delegateQueue.Count > 0) + { + result = delegateQueue.Dequeue(); + } + // Else there are no delegates waiting to be invoked. + else + { + // Wait for next delegate. + Monitor.Wait(lockObject); + + if (delegateQueue.Count > 0) + { + result = delegateQueue.Dequeue(); + } + else continue; + } + } + + System.Diagnostics.Debug.Assert(result != null); + + try + { + // Invoke the delegate. + result.Invoke(); + } + catch (TargetInvocationException e) + { + OnExceptionThrown (e); + } + } + + OnDisposed(EventArgs.Empty); + lock (lockObject) + { + while (delegateQueue.Count > 0) + { + result = delegateQueue.Dequeue(); + result.Abort(); + } + + this.DisposedEvent = null; + this.ExceptionThrownEvent = null; + this.disposed = true; + } + Debug.WriteLine(delegateThread.Name + " Finished"); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or + /// resetting unmanaged resources. + /// + /// Indicates whether Dispose was called explicitly. + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + lock (lockObject) + { + this.disposing = true; + Monitor.Pulse(lockObject); + } + } + } + } + + /// + /// Raises the Disposed event. + /// + /// The arguments to the event. + protected virtual void OnDisposed(EventArgs e) + { + EventHandler disposedEvent = DisposedEvent; + if (disposedEvent != null) + { + disposedEvent(this, e); + } + } + + protected virtual void OnExceptionThrown(TargetInvocationException ex){ + var exceptionThrownEvent = ExceptionThrownEvent; + + if (exceptionThrownEvent != null) + { + exceptionThrownEvent(this, new ThreadExceptionEventArgs(ex.InnerException)); + } + } + + #endregion + + #endregion + + #region SynchronizationContext Overrides + + /// + /// Dispatches a synchronous message to this synchronization context. + /// + /// + /// The SendOrPostCallback delegate to call. + /// + /// + /// The object passed to the delegate. + /// + /// + /// The Send method starts an synchronous request to send a message. + /// + public override void Send(SendOrPostCallback d, object state) + { + Invoke(d, state); + } + + /// + /// Dispatches an asynchronous message to this synchronization context. + /// + /// + /// The SendOrPostCallback delegate to call. + /// + /// + /// The object passed to the delegate. + /// + /// + /// The Post method starts an asynchronous request to post a message. + /// + public override void Post(SendOrPostCallback d, object state) + { + if (d == null) + { + throw new ArgumentNullException("d", new System.ArgumentNullException("d")); + } + + lock (lockObject) + { + if (disposed) + { + throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); + } + + delegateQueue.Enqueue(new DelegateQueueAsyncResult(this, d, new object[] { state })); + Monitor.Pulse(lockObject); + } + } + + #endregion + + #region IComponent Members + + /// + /// Represents the method that handles the Disposed delegate of a DelegateQueue. + /// + public event EventHandler Disposed + { + add + { + if (disposed) + { + throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); + } + this.DisposedEvent += value; + } + remove { this.DisposedEvent -= value; } + } + + + + #endregion + + #region ISynchronizeInvoke Members + + /// + /// Executes the delegate on the main thread that this DelegateQueue executes on. + /// + /// + /// A Delegate to a method that takes parameters of the same number and type that + /// are contained in args. + /// + /// + /// An array of type Object to pass as arguments to the given method. This can be + /// a null reference (Nothing in Visual Basic) if no arguments are needed. + /// + /// + /// An IAsyncResult interface that represents the asynchronous operation started + /// by calling this method. + /// + /// + /// The delegate is called asynchronously, and this method returns immediately. + /// You can call this method from any thread. If you need the return value from a process + /// started with this method, call EndInvoke to get the value. + /// If you need to call the delegate synchronously, use the Invoke method instead. + /// + public IAsyncResult BeginInvoke(Delegate method, params object[] args) + { + if (method == null) + { + throw new ArgumentNullException("method", OBJECT_DISPOSED_EXCEPTION); + } + + DelegateQueueAsyncResult result = new DelegateQueueAsyncResult(this, method, args); + lock (lockObject) + { + if (disposed) + { + throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); + } + + delegateQueue.Enqueue(result); + Monitor.Pulse(lockObject); + } + return result; + } + + /// + /// Waits until the process started by calling BeginInvoke completes, and then returns + /// the value generated by the process. + /// + /// + /// An IAsyncResult interface that represents the asynchronous operation started + /// by calling BeginInvoke. + /// + /// + /// An Object that represents the return value generated by the asynchronous operation. + /// + /// + /// This method gets the return value of the asynchronous operation represented by the + /// IAsyncResult passed by this interface. If the asynchronous operation has not completed, + /// this method will wait until the result is available. + /// + public object EndInvoke(IAsyncResult result) + { + if (disposed) + { + throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); + } + + DelegateQueueAsyncResult asyncResult = result as DelegateQueueAsyncResult; + if (asyncResult == null || asyncResult.Owner != this) + { + throw new ArgumentException("Invalid argument", "result"); + } + + asyncResult.AsyncWaitHandle.WaitOne(); + if (asyncResult.Exception != null) + { + throw asyncResult.Exception; + } + return asyncResult.ReturnValue; + } + + /// + /// Executes the delegate on the main thread that this DelegateQueue executes on. + /// + /// + /// A Delegate that contains a method to call, in the context of the thread for the DelegateQueue. + /// + /// + /// An array of type Object that represents the arguments to pass to the given method. + /// + /// + /// An Object that represents the return value from the delegate being invoked, or a + /// null reference (Nothing in Visual Basic) if the delegate has no return value. + /// + /// + /// Unlike BeginInvoke, this method operates synchronously, that is, it waits until + /// the process completes before returning. Exceptions raised during the call are propagated + /// back to the caller. + /// Use this method when calling a method from a different thread to marshal the call + /// to the proper thread. + /// + public object Invoke(Delegate method, params object[] args) + { + if (method == null) + { + throw new ArgumentNullException("method"); + } + + if (InvokeRequired) + { + DelegateQueueAsyncResult result = new DelegateQueueAsyncResult(this, method, args); + lock (lockObject) + { + if (disposed) + { + throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); + } + + delegateQueue.Enqueue(result); + Monitor.Pulse(lockObject); + } + return EndInvoke(result); + } + // Invoke the method here rather than placing it in the queue. + return method.DynamicInvoke(args); + } + + /// + /// Gets a value indicating whether the caller must call Invoke. + /// + /// + /// true if the caller must call Invoke; otherwise, false. + /// + /// + /// This property determines whether the caller must call Invoke when making + /// method calls to this DelegateQueue. If you are calling a method from a different + /// thread, you must use the Invoke method to marshal the call to the proper thread. + /// + public bool InvokeRequired + { + get + { + return Thread.CurrentThread.ManagedThreadId != delegateThread.ManagedThreadId; + } + } + + public int QueuedCount{ + get{ + lock (lockObject) { + return this.delegateQueue.Count; + } + } + } + + public bool HasMessages{ + get{ + return QueuedCount > 0; + } + } + + public Thread Thread{ + get{ + return delegateThread; + } + } + + #endregion + + #region IDisposable Members + + /// + /// Disposes of the DelegateQueue. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/OpenTK/Platform/Android/Threading/DelegateQueueAsyncResult.cs b/Source/OpenTK/Platform/Android/Threading/DelegateQueueAsyncResult.cs new file mode 100755 index 000000000..c18f89813 --- /dev/null +++ b/Source/OpenTK/Platform/Android/Threading/DelegateQueueAsyncResult.cs @@ -0,0 +1,66 @@ +using System; + +namespace OpenTK.Platform.Android.Threading +{ + /// + /// Implements the IAsyncResult interface for the DelegateQueue class. + /// + internal class DelegateQueueAsyncResult + : AsyncResult + { + // The delegate to be invoked. + private Delegate method; + + // Args to be passed to the delegate. + private object[] args; + + // The object returned from the delegate. + private object returnValue = null; + + // Represents a possible exception thrown by invoking the method. + private Exception exception = null; + + public DelegateQueueAsyncResult( + object owner, + Delegate method, + object[] args) + : base(owner, null, null) + { + this.method = method; + this.args = args; + } + + public void Invoke() + { + try + { + returnValue = method.DynamicInvoke(args); + } + catch (Exception e) + { + exception = e; + throw; + } + finally + { + Signal(); + } + } + + public void Abort() + { + exception = new OperationCanceledException(); + Signal(); + } + + public object ReturnValue + { + get { return returnValue; } + } + + public Exception Exception + { + get { return exception; } + } + } +} diff --git a/Source/OpenTK/Platform/Android/Threading/IExecutionContext.cs b/Source/OpenTK/Platform/Android/Threading/IExecutionContext.cs new file mode 100644 index 000000000..905457fe1 --- /dev/null +++ b/Source/OpenTK/Platform/Android/Threading/IExecutionContext.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel; +using System.Threading; + +namespace OpenTK.Platform.Android +{ + public interface IExecutionContext : ISynchronizeInvoke + { + event EventHandler ExecutionStateChanged; + + Thread Thread{ get;} + void Pause (); + void Stop(); + void Resume(); + void Start (); + + ExecutionState State{ get;} + } + + public class ExecutionStateChangedEventArgs : EventArgs{ + public ExecutionState State{ get; private set;} + public ExecutionState OldState{ get; private set;} + + public ExecutionStateChangedEventArgs(ExecutionState state, ExecutionState oldState){ + this.State = state; + this.OldState = oldState; + } + } + + public enum ExecutionState{ + Stop, + Executing, + Paused, + Stopping + } +} + diff --git a/Source/OpenTK/Platform/Android/Threading/Rendering_ExecutionContext_Android.cs b/Source/OpenTK/Platform/Android/Threading/Rendering_ExecutionContext_Android.cs new file mode 100644 index 000000000..cf9033bfa --- /dev/null +++ b/Source/OpenTK/Platform/Android/Threading/Rendering_ExecutionContext_Android.cs @@ -0,0 +1,154 @@ +using System; +using OpenTK.Platform.Android; +using System.Threading; + +namespace OpenTK.Platform.Android.Threading +{ + /// + /// Rendering execution context. For now it is not thread safe and it is assumed it will be executed in the UI / Main thread + /// + public class Rendering_ExecutionContext_Android : IExecutionContext + { + private BackgroundLooper context; + private ExecutionState state = ExecutionState.Stop; + + public event EventHandler ExecutionStateChanged; + public event EventHandler ExceptionThrown; + + public void Start(){ + if (context == null) { + context = new BackgroundLooper (); + context.ExceptionThrown += Context_ExceptionThrown; + OnExecutionStateChanged (ExecutionState.Executing); + context.Start (); + } + } + + + public void Pause(){ + if (context != null) { + OnExecutionStateChanged (ExecutionState.Paused); + + DrainQueuedMethods (); + + context.Pause (); + } + } + + public void Resume(){ + if (context != null) { + OnExecutionStateChanged (ExecutionState.Executing); + + context.Resume(); + } + } + + public void Stop(){ + if (context != null) { + + OnExecutionStateChanged (ExecutionState.Stopping); + + context.Resume(); + + DrainQueuedMethods (); + + OnExecutionStateChanged (ExecutionState.Stop); + + context.ExceptionThrown -= Context_ExceptionThrown; + + context.Dispose(); + + context = null; + } + } + + void Context_ExceptionThrown (object sender, ThreadExceptionEventArgs e) + { + OnExceptionThrownEvent (e.Exception); + } + + private void DrainQueuedMethods(){ + while (context.HasMessages) { + } + } + + private void AssertContext(){ + if (context == null) + throw new Exception ("Rendering execution context is not created yet"); + } + + protected virtual void OnExecutionStateChanged(ExecutionState newState){ + + if (newState == state) { + return; + } + + var oldState = state; + state = newState; + + var executionStateChanged = ExecutionStateChanged; + + if (executionStateChanged != null) { + executionStateChanged (this, new ExecutionStateChangedEventArgs (state, oldState)); + } + } + + protected virtual void OnExceptionThrownEvent(Exception error){ + var exceptionThrown = ExceptionThrown; + + if (exceptionThrown != null) { + exceptionThrown (this, new ThreadExceptionEventArgs (error)); + } + } + + public IAsyncResult BeginInvoke(Delegate method, params object[] args){ + if (state == ExecutionState.Stop || state == ExecutionState.Stopping) + return null; + + AssertContext (); + return context.BeginInvoke (method, args); + } + + public object Invoke(Delegate method, params object[] args) + { + if (state == ExecutionState.Stop || state == ExecutionState.Stopping) + return null; + + AssertContext (); + return context.Invoke (method, args); + } + + public object EndInvoke(IAsyncResult result){ + AssertContext (); + return context.EndInvoke (result); + } + + public bool InvokeRequired{ + get{ + if (context == null) + return false; + else { + return context.InvokeRequired; + } + } + } + + public Thread Thread{ + get{ + if (context == null) + return null; + else + return context.Thread; + } + } + + public ExecutionState State{ + get{ + return state; + } + } + } + + +} + From 37e38e33485342227d186e64bf805aa3300b3fb4 Mon Sep 17 00:00:00 2001 From: Nativo Labs Date: Sat, 14 Nov 2015 00:12:37 +0000 Subject: [PATCH 02/11] OpenTK project for Android was updated to support Synchronization Context --- Projects/OpenTK.Android/OpenTK.Android.csproj | 23 +++++++++++++++++++ Projects/OpenTK.Android/OpenTK.Android.sln | 6 ----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Projects/OpenTK.Android/OpenTK.Android.csproj b/Projects/OpenTK.Android/OpenTK.Android.csproj index 0f9f06a2c..2fd93581a 100644 --- a/Projects/OpenTK.Android/OpenTK.Android.csproj +++ b/Projects/OpenTK.Android/OpenTK.Android.csproj @@ -10,6 +10,8 @@ Assets Resources OpenTKA + 8.0.30703 + 2.0 true @@ -245,6 +247,12 @@ Math\Quaternion.cs + + Math\Matrix2.cs + + + Math\Matrix2d.cs + Math\Matrix3d.cs @@ -500,6 +508,21 @@ Input\ButtonState.cs + + Platform\Android\Threading\IExecutionContext.cs + + + Platform\Android\Threading\AsyncResult.cs + + + Platform\Android\Threading\BackgroundLooper.cs + + + Platform\Android\Threading\DelegateQueueAsyncResult.cs + + + Platform\Android\Threading\Rendering_ExecutionContext_Android.cs + Platform\Android\AndroidGameView.cs diff --git a/Projects/OpenTK.Android/OpenTK.Android.sln b/Projects/OpenTK.Android/OpenTK.Android.sln index 1f73006cc..bfe3ef289 100644 --- a/Projects/OpenTK.Android/OpenTK.Android.sln +++ b/Projects/OpenTK.Android/OpenTK.Android.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTK.Android", "OpenTK.Android.csproj", "{9BBB96AB-86BA-45CC-9D9A-16A1432671EE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeaderToXml", "HeaderToXml\HeaderToXml.csproj", "{F3145268-BAFF-453A-B8F6-6E199BA4BC35}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,9 +13,5 @@ Global {9BBB96AB-86BA-45CC-9D9A-16A1432671EE}.Debug|Any CPU.Build.0 = Debug|Any CPU {9BBB96AB-86BA-45CC-9D9A-16A1432671EE}.Release|Any CPU.ActiveCfg = Release|Any CPU {9BBB96AB-86BA-45CC-9D9A-16A1432671EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F3145268-BAFF-453A-B8F6-6E199BA4BC35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F3145268-BAFF-453A-B8F6-6E199BA4BC35}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F3145268-BAFF-453A-B8F6-6E199BA4BC35}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F3145268-BAFF-453A-B8F6-6E199BA4BC35}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 43c66879d3292c143887a3d8da5658f2ae1593d1 Mon Sep 17 00:00:00 2001 From: Nativo Labs Date: Sat, 14 Nov 2015 00:30:00 +0000 Subject: [PATCH 03/11] Missing file --- Source/OpenTK/Math/Matrix2d.cs | 764 +++++++++++++++++++++++++++++++++ 1 file changed, 764 insertions(+) create mode 100644 Source/OpenTK/Math/Matrix2d.cs diff --git a/Source/OpenTK/Math/Matrix2d.cs b/Source/OpenTK/Math/Matrix2d.cs new file mode 100644 index 000000000..8739ef57a --- /dev/null +++ b/Source/OpenTK/Math/Matrix2d.cs @@ -0,0 +1,764 @@ +#region --- License --- +/* +Copyright (c) 2006 - 2008 The Open Toolkit library. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ +#endregion + +using System; +using System.Runtime.InteropServices; + +namespace OpenTK +{ + /// + /// Represents a 2x2 matrix + /// + public struct Matrix2d : IEquatable + { + #region Fields + + /// + /// Top row of the matrix. + /// + public Vector2d Row0; + + /// + /// Bottom row of the matrix. + /// + public Vector2d Row1; + + /// + /// The identity matrix. + /// + public static readonly Matrix2d Identity = new Matrix2d(Vector2d.UnitX, Vector2d.UnitY); + + /// + /// The zero matrix. + /// + public static readonly Matrix2d Zero = new Matrix2d(Vector2d.Zero, Vector2d.Zero); + + #endregion + + #region Constructors + + /// + /// Constructs a new instance. + /// + /// Top row of the matrix. + /// Bottom row of the matrix. + public Matrix2d(Vector2d row0, Vector2d row1) + { + Row0 = row0; + Row1 = row1; + } + + /// + /// Constructs a new instance + /// + /// First item of the first row of the matrix. + /// Second item of the first row of the matrix. + /// First item of the second row of the matrix. + /// Second item of the second row of the matrix. + public Matrix2d( + double m00, double m01, + double m10, double m11) + { + Row0 = new Vector2d(m00, m01); + Row1 = new Vector2d(m10, m11); + } + + #endregion + + #region Public Members + + #region Properties + + /// + /// Gets the determinant of this matrix. + /// + public double Determinant + { + get + { + double m11 = Row0.X, m12 = Row0.Y, + m21 = Row1.X, m22 = Row1.Y; + + return m11 * m22 - m12 * m21; + } + } + + /// + /// Gets or sets the first column of this matrix. + /// + public Vector2d Column0 + { + get { return new Vector2d(Row0.X, Row1.X); } + set { Row0.X = value.X; Row1.X = value.Y; } + } + + /// + /// Gets or sets the second column of this matrix. + /// + public Vector2d Column1 + { + get { return new Vector2d(Row0.Y, Row1.Y); } + set { Row0.Y = value.X; Row1.Y = value.Y; } + } + + /// + /// Gets or sets the value at row 1, column 1 of this instance. + /// + public double M11 { get { return Row0.X; } set { Row0.X = value; } } + + /// + /// Gets or sets the value at row 1, column 2 of this instance. + /// + public double M12 { get { return Row0.Y; } set { Row0.Y = value; } } + + /// + /// Gets or sets the value at row 2, column 1 of this instance. + /// + public double M21 { get { return Row1.X; } set { Row1.X = value; } } + + /// + /// Gets or sets the value at row 2, column 2 of this instance. + /// + public double M22 { get { return Row1.Y; } set { Row1.Y = value; } } + + /// + /// Gets or sets the values along the main diagonal of the matrix. + /// + public Vector2d Diagonal + { + get + { + return new Vector2d(Row0.X, Row1.Y); + } + set + { + Row0.X = value.X; + Row1.Y = value.Y; + } + } + + /// + /// Gets the trace of the matrix, the sum of the values along the diagonal. + /// + public double Trace { get { return Row0.X + Row1.Y; } } + + #endregion + + #region Indexers + + /// + /// Gets or sets the value at a specified row and column. + /// +// public double this[int rowIndex, int columnIndex] +// { +// get +// { +// if (rowIndex == 0) return Row0[columnIndex]; +// else if (rowIndex == 1) return Row1[columnIndex]; +// throw new IndexOutOfRangeException("You tried to access this matrix at: (" + rowIndex + ", " + columnIndex + ")"); +// } +// set +// { +// if (rowIndex == 0) Row0[columnIndex] = value; +// else if (rowIndex == 1) Row1[columnIndex] = value; +// else throw new IndexOutOfRangeException("You tried to set this matrix at: (" + rowIndex + ", " + columnIndex + ")"); +// } +// } + + #endregion + + #region Instance + + #region public void Transpose() + + /// + /// Converts this instance to it's transpose. + /// + public void Transpose() + { + this = Matrix2d.Transpose(this); + } + + #endregion + + #region public void Invert() + + /// + /// Converts this instance into its inverse. + /// + public void Invert() + { + this = Matrix2d.Invert(this); + } + + #endregion + + #endregion + + #region Static + + #region CreateRotation + + /// + /// Builds a rotation matrix. + /// + /// The counter-clockwise angle in radians. + /// The resulting Matrix2d instance. + public static void CreateRotation(double angle, out Matrix2d result) + { + double cos = (double)System.Math.Cos(angle); + double sin = (double)System.Math.Sin(angle); + + result.Row0.X = cos; + result.Row0.Y = sin; + result.Row1.X = -sin; + result.Row1.Y = cos; + } + + /// + /// Builds a rotation matrix. + /// + /// The counter-clockwise angle in radians. + /// The resulting Matrix2d instance. + public static Matrix2d CreateRotation(double angle) + { + Matrix2d result; + CreateRotation(angle, out result); + return result; + } + + #endregion + + #region CreateScale + + /// + /// Creates a scale matrix. + /// + /// Single scale factor for the x, y, and z axes. + /// A scale matrix. + public static void CreateScale(double scale, out Matrix2d result) + { + result.Row0.X = scale; + result.Row0.Y = 0; + result.Row1.X = 0; + result.Row1.Y = scale; + } + + /// + /// Creates a scale matrix. + /// + /// Single scale factor for the x and y axes. + /// A scale matrix. + public static Matrix2d CreateScale(double scale) + { + Matrix2d result; + CreateScale(scale, out result); + return result; + } + + /// + /// Creates a scale matrix. + /// + /// Scale factors for the x and y axes. + /// A scale matrix. + public static void CreateScale(Vector2d scale, out Matrix2d result) + { + result.Row0.X = scale.X; + result.Row0.Y = 0; + result.Row1.X = 0; + result.Row1.Y = scale.Y; + } + + /// + /// Creates a scale matrix. + /// + /// Scale factors for the x and y axes. + /// A scale matrix. + public static Matrix2d CreateScale(Vector2d scale) + { + Matrix2d result; + CreateScale(scale, out result); + return result; + } + + /// + /// Creates a scale matrix. + /// + /// Scale factor for the x axis. + /// Scale factor for the y axis. + /// A scale matrix. + public static void CreateScale(double x, double y, out Matrix2d result) + { + result.Row0.X = x; + result.Row0.Y = 0; + result.Row1.X = 0; + result.Row1.Y = y; + } + + /// + /// Creates a scale matrix. + /// + /// Scale factor for the x axis. + /// Scale factor for the y axis. + /// A scale matrix. + public static Matrix2d CreateScale(double x, double y) + { + Matrix2d result; + CreateScale(x, y, out result); + return result; + } + + #endregion + + #region Multiply Functions + + /// + /// Multiplies and instance by a scalar. + /// + /// The left operand of the multiplication. + /// The right operand of the multiplication. + /// A new instance that is the result of the multiplication. + public static void Mult(ref Matrix2d left, double right, out Matrix2d result) + { + result.Row0.X = left.Row0.X * right; + result.Row0.Y = left.Row0.Y * right; + result.Row1.X = left.Row1.X * right; + result.Row1.Y = left.Row1.Y * right; + } + + /// + /// Multiplies and instance by a scalar. + /// + /// The left operand of the multiplication. + /// The right operand of the multiplication. + /// A new instance that is the result of the multiplication. + public static Matrix2d Mult(Matrix2d left, double right) + { + Matrix2d result; + Mult(ref left, right, out result); + return result; + } + + /// + /// Multiplies two instances. + /// + /// The left operand of the multiplication. + /// The right operand of the multiplication. + /// A new instance that is the result of the multiplication. + public static void Mult(ref Matrix2d left, ref Matrix2d right, out Matrix2d result) + { + double lM11 = left.Row0.X, lM12 = left.Row0.Y, + lM21 = left.Row1.X, lM22 = left.Row1.Y, + rM11 = right.Row0.X, rM12 = right.Row0.Y, + rM21 = right.Row1.X, rM22 = right.Row1.Y; + + result.Row0.X = (lM11 * rM11) + (lM12 * rM21); + result.Row0.Y = (lM11 * rM12) + (lM12 * rM22); + result.Row1.X = (lM21 * rM11) + (lM22 * rM21); + result.Row1.Y = (lM21 * rM12) + (lM22 * rM22); + } + + /// + /// Multiplies two instances. + /// + /// The left operand of the multiplication. + /// The right operand of the multiplication. + /// A new instance that is the result of the multiplication. + public static Matrix2d Mult(Matrix2d left, Matrix2d right) + { + Matrix2d result; + Mult(ref left, ref right, out result); + return result; + } + + /// + /// Multiplies two instances. + /// + /// The left operand of the multiplication. + /// The right operand of the multiplication. + /// A new instance that is the result of the multiplication. +// public static void Mult(ref Matrix2d left, ref Matrix2x3d right, out Matrix2x3d result) +// { +// double lM11 = left.Row0.X, lM12 = left.Row0.Y, +// lM21 = left.Row1.X, lM22 = left.Row1.Y, +// rM11 = right.Row0.X, rM12 = right.Row0.Y, rM13 = right.Row0.Z, +// rM21 = right.Row1.X, rM22 = right.Row1.Y, rM23 = right.Row1.Z; +// +// result.Row0.X = (lM11 * rM11) + (lM12 * rM21); +// result.Row0.Y = (lM11 * rM12) + (lM12 * rM22); +// result.Row0.Z = (lM11 * rM13) + (lM12 * rM23); +// result.Row1.X = (lM21 * rM11) + (lM22 * rM21); +// result.Row1.Y = (lM21 * rM12) + (lM22 * rM22); +// result.Row1.Z = (lM21 * rM13) + (lM22 * rM23); +// } +// +// /// +// /// Multiplies two instances. +// /// +// /// The left operand of the multiplication. +// /// The right operand of the multiplication. +// /// A new instance that is the result of the multiplication. +// public static Matrix2x3d Mult(Matrix2d left, Matrix2x3d right) +// { +// Matrix2x3d result; +// Mult(ref left, ref right, out result); +// return result; +// } +// +// /// +// /// Multiplies two instances. +// /// +// /// The left operand of the multiplication. +// /// The right operand of the multiplication. +// /// A new instance that is the result of the multiplication. +// public static void Mult(ref Matrix2d left, ref Matrix2x4d right, out Matrix2x4d result) +// { +// double lM11 = left.Row0.X, lM12 = left.Row0.Y, +// lM21 = left.Row1.X, lM22 = left.Row1.Y, +// rM11 = right.Row0.X, rM12 = right.Row0.Y, rM13 = right.Row0.Z, rM14 = right.Row0.W, +// rM21 = right.Row1.X, rM22 = right.Row1.Y, rM23 = right.Row1.Z, rM24 = right.Row1.W; +// +// result.Row0.X = (lM11 * rM11) + (lM12 * rM21); +// result.Row0.Y = (lM11 * rM12) + (lM12 * rM22); +// result.Row0.Z = (lM11 * rM13) + (lM12 * rM23); +// result.Row0.W = (lM11 * rM14) + (lM12 * rM24); +// result.Row1.X = (lM21 * rM11) + (lM22 * rM21); +// result.Row1.Y = (lM21 * rM12) + (lM22 * rM22); +// result.Row1.Z = (lM21 * rM13) + (lM22 * rM23); +// result.Row1.W = (lM21 * rM14) + (lM22 * rM24); +// } +// +// /// +// /// Multiplies two instances. +// /// +// /// The left operand of the multiplication. +// /// The right operand of the multiplication. +// /// A new instance that is the result of the multiplication. +// public static Matrix2x4d Mult(Matrix2d left, Matrix2x4d right) +// { +// Matrix2x4d result; +// Mult(ref left, ref right, out result); +// return result; +// } + + #endregion + + #region Add + + /// + /// Adds two instances. + /// + /// The left operand of the addition. + /// The right operand of the addition. + /// A new instance that is the result of the addition. + public static void Add(ref Matrix2d left, ref Matrix2d right, out Matrix2d result) + { + result.Row0.X = left.Row0.X + right.Row0.X; + result.Row0.Y = left.Row0.Y + right.Row0.Y; + result.Row1.X = left.Row1.X + right.Row1.X; + result.Row1.Y = left.Row1.Y + right.Row1.Y; + } + + /// + /// Adds two instances. + /// + /// The left operand of the addition. + /// The right operand of the addition. + /// A new instance that is the result of the addition. + public static Matrix2d Add(Matrix2d left, Matrix2d right) + { + Matrix2d result; + Add(ref left, ref right, out result); + return result; + } + + #endregion + + #region Subtract + + /// + /// Subtracts two instances. + /// + /// The left operand of the subtraction. + /// The right operand of the subtraction. + /// A new instance that is the result of the subtraction. + public static void Subtract(ref Matrix2d left, ref Matrix2d right, out Matrix2d result) + { + result.Row0.X = left.Row0.X - right.Row0.X; + result.Row0.Y = left.Row0.Y - right.Row0.Y; + result.Row1.X = left.Row1.X - right.Row1.X; + result.Row1.Y = left.Row1.Y - right.Row1.Y; + } + + /// + /// Subtracts two instances. + /// + /// The left operand of the subtraction. + /// The right operand of the subtraction. + /// A new instance that is the result of the subtraction. + public static Matrix2d Subtract(Matrix2d left, Matrix2d right) + { + Matrix2d result; + Subtract(ref left, ref right, out result); + return result; + } + + #endregion + + #region Invert Functions + + /// + /// Calculate the inverse of the given matrix + /// + /// The matrix to invert + /// The inverse of the given matrix if it has one, or the input if it is singular + /// Thrown if the Matrix2d is singular. + public static void Invert(ref Matrix2d mat, out Matrix2d result) + { + double det = mat.Determinant; + + if (det == 0) + throw new InvalidOperationException("Matrix is singular and cannot be inverted."); + + double invDet = 1f / det; + + result.Row0.X = mat.Row1.Y * invDet; + result.Row0.Y = -mat.Row0.Y * invDet; + result.Row1.X = -mat.Row1.X * invDet; + result.Row1.Y = mat.Row0.X * invDet; + } + + /// + /// Calculate the inverse of the given matrix + /// + /// The matrix to invert + /// The inverse of the given matrix if it has one, or the input if it is singular + /// Thrown if the Matrix2d is singular. + public static Matrix2d Invert(Matrix2d mat) + { + Matrix2d result; + Invert(ref mat, out result); + return result; + } + + #endregion + + #region Transpose + + /// + /// Calculate the transpose of the given matrix. + /// + /// The matrix to transpose. + /// The transpose of the given matrix. + public static void Transpose(ref Matrix2d mat, out Matrix2d result) + { + result.Row0.X = mat.Row0.X; + result.Row0.Y = mat.Row1.X; + result.Row1.X = mat.Row0.Y; + result.Row1.Y = mat.Row1.Y; + } + + /// + /// Calculate the transpose of the given matrix. + /// + /// The matrix to transpose. + /// The transpose of the given matrix. + public static Matrix2d Transpose(Matrix2d mat) + { + Matrix2d result; + Transpose(ref mat, out result); + return result; + } + + #endregion + + #endregion + + #region Operators + + /// + /// Scalar multiplication. + /// + /// left-hand operand + /// right-hand operand + /// A new Matrix2d which holds the result of the multiplication + public static Matrix2d operator *(double left, Matrix2d right) + { + return Mult(right, left); + } + + /// + /// Scalar multiplication. + /// + /// left-hand operand + /// right-hand operand + /// A new Matrix2d which holds the result of the multiplication + public static Matrix2d operator *(Matrix2d left, double right) + { + return Mult(left, right); + } + + /// + /// Matrix multiplication + /// + /// left-hand operand + /// right-hand operand + /// A new Matrix2d which holds the result of the multiplication + public static Matrix2d operator *(Matrix2d left, Matrix2d right) + { + return Mult(left, right); + } + + /// + /// Matrix multiplication + /// + /// left-hand operand + /// right-hand operand + /// A new Matrix2x3d which holds the result of the multiplication +// public static Matrix2x3d operator *(Matrix2d left, Matrix2x3d right) +// { +// return Mult(left, right); +// } +// +// /// +// /// Matrix multiplication +// /// +// /// left-hand operand +// /// right-hand operand +// /// A new Matrix2x4d which holds the result of the multiplication +// public static Matrix2x4d operator *(Matrix2d left, Matrix2x4d right) +// { +// return Mult(left, right); +// } + + /// + /// Matrix addition + /// + /// left-hand operand + /// right-hand operand + /// A new Matrix2d which holds the result of the addition + public static Matrix2d operator +(Matrix2d left, Matrix2d right) + { + return Add(left, right); + } + + /// + /// Matrix subtraction + /// + /// left-hand operand + /// right-hand operand + /// A new Matrix2d which holds the result of the subtraction + public static Matrix2d operator -(Matrix2d left, Matrix2d right) + { + return Subtract(left, right); + } + + /// + /// Compares two instances for equality. + /// + /// The first instance. + /// The second instance. + /// True, if left equals right; false otherwise. + public static bool operator ==(Matrix2d left, Matrix2d right) + { + return left.Equals(right); + } + + /// + /// Compares two instances for inequality. + /// + /// The first instance. + /// The second instance. + /// True, if left does not equal right; false otherwise. + public static bool operator !=(Matrix2d left, Matrix2d right) + { + return !left.Equals(right); + } + + #endregion + + #region Overrides + + #region public override string ToString() + + /// + /// Returns a System.String that represents the current Matrix4. + /// + /// The string representation of the matrix. + public override string ToString() + { + return String.Format("{0}\n{1}", Row0, Row1); + } + + #endregion + + #region public override int GetHashCode() + + /// + /// Returns the hashcode for this instance. + /// + /// A System.Int32 containing the unique hashcode for this instance. + public override int GetHashCode() + { + return Row0.GetHashCode() ^ Row1.GetHashCode(); + } + + #endregion + + #region public override bool Equals(object obj) + + /// + /// Indicates whether this instance and a specified object are equal. + /// + /// The object to compare to. + /// True if the instances are equal; false otherwise. + public override bool Equals(object obj) + { + if (!(obj is Matrix2d)) + return false; + + return this.Equals((Matrix2d)obj); + } + + #endregion + + #endregion + + #endregion + + #region IEquatable Members + + /// Indicates whether the current matrix is equal to another matrix. + /// An matrix to compare with this matrix. + /// true if the current matrix is equal to the matrix parameter; otherwise, false. + public bool Equals(Matrix2d other) + { + return + Row0 == other.Row0 && + Row1 == other.Row1; + } + + #endregion + } +} From db24bd800805171f1f3e0a2987190ec140d80a39 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Sat, 14 Nov 2015 02:02:28 +0000 Subject: [PATCH 04/11] Create README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..5e75d3d23 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# opentk +OpenTK is a set of bindings to OpenGL, OpenCL and OpenAL. This is not the main repository, it extendes OpenTK for Android to work with a dedicated rendering execution context. To do so, we used a specific looper (BackgroundLooper) that extends SynchronizationContext class. +It is important to note that SynchronizationContext is the fundamental component of the async / await patern. For details, you can check [Parallel Programming with .Net - ExecutionContext vs SynchronizationContext](http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx) +When the Activity is Paused or Stopped surface and window are destroyed and to prevent loosing OpenGL context, AndroidGameView detaches it from the window and Surface (for details, please, look at this [discussion](http://forums.xamarin.com/discussion/621/androidgameview-pause-without-losing-gl-context/p2). +To guarantee that no OpenGL calls are made when the Activity is Paused or Stopped, we created an interface (IExecutionContext) with method Pause to stop the execution of the looper and Resume to reinitialize the execution of queued methods. This interface is used by AndroidGameView and Rendering_ExecutionContext_Android is an implementation that we provide that essentially do a set of other things on top of BackgroundLooper. From 4a0931b22626830f06d281a0ec6785fa38a74d67 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Sat, 14 Nov 2015 02:34:43 +0000 Subject: [PATCH 05/11] Update README.md --- README.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e75d3d23..7d23a286e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,54 @@ -# opentk +# AndroidGameView with async / await compatability on a dedicated thread + +##Overview OpenTK is a set of bindings to OpenGL, OpenCL and OpenAL. This is not the main repository, it extendes OpenTK for Android to work with a dedicated rendering execution context. To do so, we used a specific looper (BackgroundLooper) that extends SynchronizationContext class. It is important to note that SynchronizationContext is the fundamental component of the async / await patern. For details, you can check [Parallel Programming with .Net - ExecutionContext vs SynchronizationContext](http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx) When the Activity is Paused or Stopped surface and window are destroyed and to prevent loosing OpenGL context, AndroidGameView detaches it from the window and Surface (for details, please, look at this [discussion](http://forums.xamarin.com/discussion/621/androidgameview-pause-without-losing-gl-context/p2). To guarantee that no OpenGL calls are made when the Activity is Paused or Stopped, we created an interface (IExecutionContext) with method Pause to stop the execution of the looper and Resume to reinitialize the execution of queued methods. This interface is used by AndroidGameView and Rendering_ExecutionContext_Android is an implementation that we provide that essentially do a set of other things on top of BackgroundLooper. + +Note : in this implementation we do not provide any timer to periodically call OnRenderFrame like it happens with Run method + +##How to use + +1 - Create a normal OpenGL Application from the template provided by Xamarin Studio + +2 - Replace OpenTk by a version compiled with the code available in this repository + +3 - Change the creation of GLView1 (AndroidGameView child) to something like + + + public class MainActivity : Activity + { + GLView1 view; + Rendering_ExecutionContext_Android rendering_ExecutionContext; + + protected override void OnCreate (Bundle bundle) + { + base.OnCreate (bundle); + + rendering_ExecutionContext = new Rendering_ExecutionContext_Android (); + + //Starts the looper + rendering_ExecutionContext.Start (); + + view = new GLView1 (this); + view.RenderingExecutionContext = rendering_ExecutionContext; + + + SetContentView (view); + + } + } + +4 - Remove the call to Run in OnLoad overridden method + +5 - To Execute on Rendering thread + + rendering_ExecutionContext.BeginInvoke(new Action(async()=>{ + await ... + }), null); + + rendering_ExecutionContext.BeginInvoke(new Action(()=>{ + + }), null); + From 3a5b12ce23c0433dc2e338045e5be910125fc6d5 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Sat, 14 Nov 2015 03:06:13 +0000 Subject: [PATCH 06/11] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7d23a286e..074305dd5 100644 --- a/README.md +++ b/README.md @@ -20,16 +20,17 @@ Note : in this implementation we do not provide any timer to periodically call O public class MainActivity : Activity { GLView1 view; - Rendering_ExecutionContext_Android rendering_ExecutionContext; + //To gurantee that the looper is always alive + static Rendering_ExecutionContext_Android rendering_ExecutionContext = new Rendering_ExecutionContext_Android(); protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); - rendering_ExecutionContext = new Rendering_ExecutionContext_Android (); //Starts the looper - rendering_ExecutionContext.Start (); + if(rendering_ExecutionContext.State != ExecutionState.Executing) + rendering_ExecutionContext.Start (); view = new GLView1 (this); view.RenderingExecutionContext = rendering_ExecutionContext; From cb818d150f7b565dd18c69c7d4e40a725415fd79 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Sat, 14 Nov 2015 15:55:24 +0000 Subject: [PATCH 07/11] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 074305dd5..bb1e23f6a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # AndroidGameView with async / await compatability on a dedicated thread ##Overview -OpenTK is a set of bindings to OpenGL, OpenCL and OpenAL. This is not the main repository, it extendes OpenTK for Android to work with a dedicated rendering execution context. To do so, we used a specific looper (BackgroundLooper) that extends SynchronizationContext class. +OpenTK is a set of bindings to OpenGL, OpenCL and OpenAL. This is not the main repository, it extendes OpenTK for Android to work with a dedicated rendering execution context. To do so, we used a specific looper ([BackgroundLooper](http://www.codeproject.com/Articles/12082/A-DelegateQueue-Class)) that extends SynchronizationContext class. It is important to note that SynchronizationContext is the fundamental component of the async / await patern. For details, you can check [Parallel Programming with .Net - ExecutionContext vs SynchronizationContext](http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx) When the Activity is Paused or Stopped surface and window are destroyed and to prevent loosing OpenGL context, AndroidGameView detaches it from the window and Surface (for details, please, look at this [discussion](http://forums.xamarin.com/discussion/621/androidgameview-pause-without-losing-gl-context/p2). To guarantee that no OpenGL calls are made when the Activity is Paused or Stopped, we created an interface (IExecutionContext) with method Pause to stop the execution of the looper and Resume to reinitialize the execution of queued methods. This interface is used by AndroidGameView and Rendering_ExecutionContext_Android is an implementation that we provide that essentially do a set of other things on top of BackgroundLooper. From e9b64052c265eb41476b09710937cff8994465b1 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Sat, 14 Nov 2015 16:00:30 +0000 Subject: [PATCH 08/11] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb1e23f6a..5492952ec 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ OpenTK is a set of bindings to OpenGL, OpenCL and OpenAL. This is not the main repository, it extendes OpenTK for Android to work with a dedicated rendering execution context. To do so, we used a specific looper ([BackgroundLooper](http://www.codeproject.com/Articles/12082/A-DelegateQueue-Class)) that extends SynchronizationContext class. It is important to note that SynchronizationContext is the fundamental component of the async / await patern. For details, you can check [Parallel Programming with .Net - ExecutionContext vs SynchronizationContext](http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx) When the Activity is Paused or Stopped surface and window are destroyed and to prevent loosing OpenGL context, AndroidGameView detaches it from the window and Surface (for details, please, look at this [discussion](http://forums.xamarin.com/discussion/621/androidgameview-pause-without-losing-gl-context/p2). -To guarantee that no OpenGL calls are made when the Activity is Paused or Stopped, we created an interface (IExecutionContext) with method Pause to stop the execution of the looper and Resume to reinitialize the execution of queued methods. This interface is used by AndroidGameView and Rendering_ExecutionContext_Android is an implementation that we provide that essentially do a set of other things on top of BackgroundLooper. +To guarantee that no OpenGL calls are made when the Activity is Paused or Stopped, we created an interface (IExecutionContext) with method Pause to stop the execution of the looper and Resume to reinitialize the execution of queued methods. This interface is used by AndroidGameView and Rendering_ExecutionContext_Android is an implementation that we provide that essentially do a set of other small things on top of BackgroundLooper like execute all queued methods before pausing the looper. Note : in this implementation we do not provide any timer to periodically call OnRenderFrame like it happens with Run method From ae121fb5c555a989f05cbeee25e160d8aa8ac60d Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Mon, 16 Nov 2015 11:07:10 +0000 Subject: [PATCH 09/11] Update README.md --- README.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5492952ec..af09d187f 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ -# AndroidGameView with async / await compatability on a dedicated thread +# AndroidGameView with async / await compatibility on a dedicated thread ##Overview -OpenTK is a set of bindings to OpenGL, OpenCL and OpenAL. This is not the main repository, it extendes OpenTK for Android to work with a dedicated rendering execution context. To do so, we used a specific looper ([BackgroundLooper](http://www.codeproject.com/Articles/12082/A-DelegateQueue-Class)) that extends SynchronizationContext class. +OpenTK is a set of bindings to OpenGL, OpenCL and OpenAL. This is not the main repository, it extends OpenTK for Android to work with a dedicated rendering execution context. To do so, we used a specific looper ([BackgroundLooper](http://www.codeproject.com/Articles/12082/A-DelegateQueue-Class)) that extends SynchronizationContext class. It is important to note that SynchronizationContext is the fundamental component of the async / await patern. For details, you can check [Parallel Programming with .Net - ExecutionContext vs SynchronizationContext](http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx) When the Activity is Paused or Stopped surface and window are destroyed and to prevent loosing OpenGL context, AndroidGameView detaches it from the window and Surface (for details, please, look at this [discussion](http://forums.xamarin.com/discussion/621/androidgameview-pause-without-losing-gl-context/p2). To guarantee that no OpenGL calls are made when the Activity is Paused or Stopped, we created an interface (IExecutionContext) with method Pause to stop the execution of the looper and Resume to reinitialize the execution of queued methods. This interface is used by AndroidGameView and Rendering_ExecutionContext_Android is an implementation that we provide that essentially do a set of other small things on top of BackgroundLooper like execute all queued methods before pausing the looper. Note : in this implementation we do not provide any timer to periodically call OnRenderFrame like it happens with Run method + ##How to use 1 - Create a normal OpenGL Application from the template provided by Xamarin Studio @@ -43,7 +44,7 @@ Note : in this implementation we do not provide any timer to periodically call O 4 - Remove the call to Run in OnLoad overridden method -5 - To Execute on Rendering thread +5 - To Execute on Rendering thread. rendering_ExecutionContext.BeginInvoke(new Action(async()=>{ await ... @@ -53,3 +54,9 @@ Note : in this implementation we do not provide any timer to periodically call O }), null); + +##Important Note + + +Do not execute GL instructions before OnLoad method occurs. +This could be prevented by starting the looper when this method is called, but it could be useful to have the looper running to do other things that do not involve GL calls and you need them running on the same thread as OpenGL. From 1feb3765b3b49a748039de9831e347c838cd4f0d Mon Sep 17 00:00:00 2001 From: Nativo Labs Date: Mon, 16 Nov 2015 16:51:05 +0000 Subject: [PATCH 10/11] AndroidGameView was working with 1_1, but this was left forgotten and it was working. It seems that mono.android.jar contains opentk/GameViewBase.class and opentk_1_0/GameViewBase.class (mono.android.jar contains opentk/GameViewBase.class and opentk_1_0/GameViewBase.class). --- Source/OpenTK/Platform/Android/GameViewBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/OpenTK/Platform/Android/GameViewBase.cs b/Source/OpenTK/Platform/Android/GameViewBase.cs index a8ad1053d..75a27c16c 100644 --- a/Source/OpenTK/Platform/Android/GameViewBase.cs +++ b/Source/OpenTK/Platform/Android/GameViewBase.cs @@ -33,7 +33,7 @@ namespace OpenTK #if OPENTK_0 [Register ("opentk/GameViewBase")] #else - [Register ("opentk_1_0/GameViewBase")] + [Register ("opentk_1_1/GameViewBase")] #endif public abstract class GameViewBase : SurfaceView, IGameWindow { From 3ce0e7d3c447267d8184a6ecd49be08def47d4b9 Mon Sep 17 00:00:00 2001 From: Nativo Labs Date: Thu, 25 Feb 2016 15:30:36 +0000 Subject: [PATCH 11/11] BackgroundLooper - Pause, Resume bug correction --- .../Platform/Android/Threading/BackgroundLooper.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/OpenTK/Platform/Android/Threading/BackgroundLooper.cs b/Source/OpenTK/Platform/Android/Threading/BackgroundLooper.cs index ecd649dd1..3be03ca84 100755 --- a/Source/OpenTK/Platform/Android/Threading/BackgroundLooper.cs +++ b/Source/OpenTK/Platform/Android/Threading/BackgroundLooper.cs @@ -271,9 +271,6 @@ public void Start() } } - /// - /// Pauses delegate execution in the delegate queue. - /// public void Pause() { if (disposed) @@ -281,7 +278,9 @@ public void Pause() throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); } - Monitor.Enter(lockObject); + if (!Monitor.IsEntered (lockObject)) { + Monitor.Enter (lockObject); + } } /// @@ -294,7 +293,9 @@ public void Resume() throw new ObjectDisposedException(string.Empty, OBJECT_DISPOSED_EXCEPTION); } - Monitor.Exit(lockObject); + if(Monitor.IsEntered(lockObject)){ + Monitor.Exit (lockObject); + } } // Processes and invokes delegates.