context: #region Copyright & License Information
  /*
   * Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation, either version 3 of
   * the License, or (at your option) any later version. For more
   * information, see COPYING.
   */
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.Runtime.ExceptionServices;
  using System.Threading;
  using OpenRA.Graphics;
  using OpenRA.Primitives;
  
  namespace OpenRA.Platforms.Default
  {
  	/// <summary>
  	/// Creates a dedicated thread for the graphics device. An internal message queue is used to perform actions on the
  	/// device. This allows calls to be enqueued to be processed asynchronously and thus free up the calling thread.
  	/// </summary>
  	sealed class ThreadedGraphicsContext : IGraphicsContext
  	{
  		// PERF: Maintain several object pools to reduce allocations.
  		readonly Stack<Vertex[]> verticesPool = new Stack<Vertex[]>();
  		readonly Stack<Message> messagePool = new Stack<Message>();
  		readonly Queue<Message> messages = new Queue<Message>();
  
  		public readonly int BatchSize;
  		readonly object syncObject = new object();
  		readonly Thread renderThread;
  		volatile ExceptionDispatchInfo messageException;
  
  		// Delegates that perform actions on the real device.
  		Func<object> doClear;
  		Action doClearDepthBuffer;
  		Action doDisableDepthBuffer;
  		Action doEnableDepthBuffer;
  		Action doDisableScissor;
  		Action doPresent;
  		Func<string> getGLVersion;
  		Func<ITexture> getCreateTexture;
  		Func<object, IFrameBuffer> getCreateFrameBuffer;
  		Func<object, IShader> getCreateShader;
  		Func<object, IVertexBuffer<Vertex>> getCreateVertexBuffer;
  		Action<object> doDrawPrimitives;
  		Action<object> doEnableScissor;
  		Action<object> doSetBlendMode;
  		Action<object> doSetVSync;
  
  		public ThreadedGraphicsContext(Sdl2GraphicsContext context, int batchSize)
  		{
  			BatchSize = batchSize;
  			renderThread = new Thread(RenderThread)
  			{
  				Name = "ThreadedGraphicsContext RenderThread",
  				IsBackground = true
  			};
  			lock (syncObject)
  			{
  				// Start and wait for the rendering thread to have initialized before returning.
  				// Otherwise, the delegates may not have been set yet.
  				renderThread.Start(context);
  				Monitor.Wait(syncObject);
  			}
  		}
  
  		void RenderThread(object contextObject)
  		{
  			using (var context = (Sdl2GraphicsContext)contextObject)
  			{
  				// This lock allows the constructor to block until initialization completes.
  				lock (syncObject)
  				{
  					context.InitializeOpenGL();
  
  					doClear = () => { context.Clear(); return null; };
  					doClearDepthBuffer = () => context.ClearDepthBuffer();
  					doDisableDepthBuffer = () => context.DisableDepthBuffer();
  					doEnableDepthBuffer = () => context.EnableDepthBuffer();
  					doDisableScissor = () => context.DisableScissor();
  					doPresent = () => context.Present();
  					getGLVersion = () => context.GLVersion;
  					getCreateTexture = () => new ThreadedTexture(this, (ITextureInternal)context.CreateTexture());
  					getCreateFrameBuffer =
  						tuple =>
  						{
  							var t = (ValueTuple<Size, Color>)tuple;
  							return new ThreadedFrameBuffer(this,
  								context.CreateFrameBuffer(t.Item1, (ITextureInternal)CreateTexture(), t.Item2));
  						};
  					getCreateShader = name => new ThreadedShader(this, context.CreateShader((string)name));
  					getCreateVertexBuffer = length => new ThreadedVertexBuffer(this, context.CreateVertexBuffer((int)length));
  					doDrawPrimitives =
  						 tuple =>
  						 {
  							 var t = (ValueTuple<PrimitiveType, int, int>)tuple;
  							 context.DrawPrimitives(t.Item1, t.Item2, t.Item3);
  						 };
  					doEnableScissor =
  						tuple =>
  						{
  							var t = (ValueTuple<int, int, int, int>)tuple;
  							context.EnableScissor(t.Item1, t.Item2, t.Item3, t.Item4);
  						};
  					doSetBlendMode = mode => { context.SetBlendMode((BlendMode)mode); };
  					doSetVSync = enabled => { context.SetVSyncEnabled((bool)enabled); };
  
  					Monitor.Pulse(syncObject);
  				}
  
  				// Run a message loop.
  				// Only this rendering thread can perform actions on the real device,
  				// so other threads must send us a message which we process here.
  				Message message;
  				while (true)
  				{
  					lock (messages)
  					{
  						if (messages.Count == 0)
  						{
  							if (messageException != null)
  								break;
  
  							Monitor.Wait(messages);
  						}
  
  						message = messages.Dequeue();
  					}
  
  					if (message == null)
  						break;
  
  					message.Execute();
  				}
  			}
  		}
  
  		internal Vertex[] GetVertices(int size)
  		{
  			lock (verticesPool)
  				if (size <= BatchSize && verticesPool.Count > 0)
  					return verticesPool.Pop();
  
  			return new Vertex[size < BatchSize ? BatchSize : size];
  		}
  
  		internal void ReturnVertices(Vertex[] vertices)
  		{
  			if (vertices.Length == BatchSize)
  				lock (verticesPool)
  					verticesPool.Push(vertices);
  		}
  
  		class Message
  		{
  			public Message(ThreadedGraphicsContext device)
  			{
  				this.device = device;
  			}
  
  			readonly AutoResetEvent completed = new AutoResetEvent(false);
  			readonly ThreadedGraphicsContext device;
  			volatile Action action;
  			volatile Action<object> actionWithParam;
  			volatile Func<object> func;
  			volatile Func<object, object> funcWithParam;
  			volatile object param;
  			volatile object result;
  			volatile ExceptionDispatchInfo edi;
  
  			public void SetAction(Action method)
  			{
  				action = method;
  			}
  
  			public void SetAction(Action<object> method, object state)
  			{
  				actionWithParam = method;
  				param = state;
  			}
  
  			public void SetAction(Func<object> method)
  			{
  				func = method;
  			}
  
  			public void SetAction(Func<object, object> method, object state)
  			{
  				funcWithParam = method;
  				param = state;
  			}
  
  			public void Execute()
  			{
  				var wasSend = action != null || actionWithParam != null;
  				try
  				{
  
gt: 					if (action != null)