#region License /* FNA - XNA4 Reimplementation for Desktop Platforms * Copyright 2009-2020 Ethan Lee and the MonoGame Team * * Released under the Microsoft Public License. * See LICENSE for details. */ #endregion #region Using Statements using System; using System.Threading; using System.Collections.Generic; using System.Collections.Concurrent; #endregion namespace Microsoft.Xna.Framework.Graphics { internal class ThreadedGLDevice : IGLDevice { #region Public Properties public Color BlendFactor { get { Color result = Color.Black; ForceToMainThread(() => { result = GLDevice.BlendFactor; }); // End ForceToMainThread return result; } set { ForceToMainThread(() => { GLDevice.BlendFactor = value; }); // End ForceToMainThread } } public int MultiSampleMask { get { int result = 0; ForceToMainThread(() => { result = GLDevice.MultiSampleMask; }); // End ForceToMainThread return result; } set { ForceToMainThread(() => { GLDevice.MultiSampleMask = value; }); // End ForceToMainThread } } public int ReferenceStencil { get { int result = 0; ForceToMainThread(() => { result = GLDevice.ReferenceStencil; }); // End ForceToMainThread return result; } set { ForceToMainThread(() => { GLDevice.ReferenceStencil = value; }); // End ForceToMainThread } } public bool SupportsDxt1 { get { bool result = false; ForceToMainThread(() => { result = GLDevice.SupportsDxt1; }); // End ForceToMainThread return result; } } public bool SupportsS3tc { get { bool result = false; ForceToMainThread(() => { result = GLDevice.SupportsS3tc; }); // End ForceToMainThread return result; } } public bool SupportsHardwareInstancing { get { bool result = false; ForceToMainThread(() => { result = GLDevice.SupportsHardwareInstancing; }); // End ForceToMainThread return result; } } public bool SupportsNoOverwrite { get { bool result = false; ForceToMainThread(() => { result = GLDevice.SupportsNoOverwrite; }); // End ForceToMainThread return result; } } public int MaxTextureSlots { get { int result = 0; ForceToMainThread(() => { result = GLDevice.MaxTextureSlots; }); // End ForceToMainThread return result; } } public int MaxMultiSampleCount { get { int result = 0; ForceToMainThread(() => { result = GLDevice.MaxMultiSampleCount; }); // End ForceToMainThread return result; } } public IGLBackbuffer Backbuffer { get { IGLBackbuffer result = null; ForceToMainThread(() => { result = GLDevice.Backbuffer; }); // End ForceToMainThread return result; } } #endregion #region Private Variables private IGLDevice GLDevice; private Thread csThread; private bool csDone = false; private AutoResetEvent csEvent = new AutoResetEvent(false); private List actions = new List(); #endregion #region Private Graphics Object Disposal Queues private ConcurrentQueue GCTextures = new ConcurrentQueue(); private ConcurrentQueue GCRenderbuffers = new ConcurrentQueue(); private ConcurrentQueue GCVertexBuffers = new ConcurrentQueue(); private ConcurrentQueue GCIndexBuffers = new ConcurrentQueue(); private ConcurrentQueue GCEffects = new ConcurrentQueue(); private ConcurrentQueue GCQueries = new ConcurrentQueue(); #endregion #region Thread Functions private void csThreadProc() { while (!csDone) { csEvent.WaitOne(); lock (actions) { foreach (Action action in actions) { action(); } actions.Clear(); } } GLDevice.Dispose(); } private void ForceToMainThread(Action action) { ManualResetEventSlim resetEvent = new ManualResetEventSlim(false); lock (actions) { actions.Add(() => { action(); resetEvent.Set(); }); } csEvent.Set(); resetEvent.Wait(); } #endregion #region Constructor/Disposal public ThreadedGLDevice(PresentationParameters presentationParameters) { csThread = new Thread(new ThreadStart(csThreadProc)); csThread.Start(); FNALoggerEXT.LogInfo("Running with ThreadedGLDevice!"); ForceToMainThread(() => { if (Environment.GetEnvironmentVariable( "FNA_THREADEDGLDEVICE_GLDEVICE" ) == "OpenGLDevice") { GLDevice = new OpenGLDevice( presentationParameters ); } else { GLDevice = new ModernGLDevice( presentationParameters ); } }); // End ForceToMainThread } public void Dispose() { ForceToMainThread(() => { csDone = true; }); // End ForceToMainThread csThread.Join(); } #endregion #region BeginFrame Operations public void BeginFrame() { ForceToMainThread(() => { GLDevice.BeginFrame(); }); // End ForceToMainThread } #endregion #region Backbuffer Operations public void ResetBackbuffer(PresentationParameters presentationParameters) { ForceToMainThread(() => { GLDevice.ResetBackbuffer( presentationParameters ); }); // End ForceToMainThread } public void SwapBuffers( Rectangle? sourceRectangle, Rectangle? destinationRectangle, IntPtr overrideWindowHandle ) { ForceToMainThread(() => { GLDevice.SwapBuffers( sourceRectangle, destinationRectangle, overrideWindowHandle ); IGLTexture gcTexture; while (GCTextures.TryDequeue(out gcTexture)) { GLDevice.AddDisposeTexture(gcTexture); } IGLRenderbuffer gcRenderbuffer; while (GCRenderbuffers.TryDequeue(out gcRenderbuffer)) { GLDevice.AddDisposeRenderbuffer(gcRenderbuffer); } IGLBuffer gcBuffer; while (GCVertexBuffers.TryDequeue(out gcBuffer)) { GLDevice.AddDisposeVertexBuffer(gcBuffer); } while (GCIndexBuffers.TryDequeue(out gcBuffer)) { GLDevice.AddDisposeIndexBuffer(gcBuffer); } IGLEffect gcEffect; while (GCEffects.TryDequeue(out gcEffect)) { GLDevice.AddDisposeEffect(gcEffect); } IGLQuery gcQuery; while (GCQueries.TryDequeue(out gcQuery)) { GLDevice.AddDisposeQuery(gcQuery); } }); // End ForceToMainThread } #endregion #region SetStringMarkerEXT public void SetStringMarker(string text) { ForceToMainThread(() => { GLDevice.SetStringMarker(text); }); // End ForceToMainThread } #endregion #region Draw Calls public void DrawIndexedPrimitives( PrimitiveType primitiveType, int baseVertex, int minVertexIndex, int numVertices, int startIndex, int primitiveCount, IGLBuffer indices, IndexElementSize indexElementSize ) { ForceToMainThread(() => { GLDevice.DrawIndexedPrimitives( primitiveType, baseVertex, minVertexIndex, numVertices, startIndex, primitiveCount, indices, indexElementSize ); }); // End ForceToMainThread } public void DrawInstancedPrimitives( PrimitiveType primitiveType, int baseVertex, int minVertexIndex, int numVertices, int startIndex, int primitiveCount, int instanceCount, IGLBuffer indices, IndexElementSize indexElementSize ) { ForceToMainThread(() => { GLDevice.DrawInstancedPrimitives( primitiveType, baseVertex, minVertexIndex, numVertices, startIndex, primitiveCount, instanceCount, indices, indexElementSize ); }); // End ForceToMainThread } public void DrawPrimitives( PrimitiveType primitiveType, int vertexStart, int primitiveCount ) { ForceToMainThread(() => { GLDevice.DrawPrimitives( primitiveType, vertexStart, primitiveCount ); }); // End ForceToMainThread } public void DrawUserIndexedPrimitives( PrimitiveType primitiveType, IntPtr vertexData, int vertexOffset, int numVertices, IntPtr indexData, int indexOffset, IndexElementSize indexElementSize, int primitiveCount ) { ForceToMainThread(() => { GLDevice.DrawUserIndexedPrimitives( primitiveType, vertexData, vertexOffset, numVertices, indexData, indexOffset, indexElementSize, primitiveCount ); }); // End ForceToMainThread } public void DrawUserPrimitives( PrimitiveType primitiveType, IntPtr vertexData, int vertexOffset, int primitiveCount ) { ForceToMainThread(() => { GLDevice.DrawUserPrimitives( primitiveType, vertexData, vertexOffset, primitiveCount ); }); // End ForceToMainThread } #endregion #region Render States public void SetPresentationInterval(PresentInterval presentInterval) { ForceToMainThread(() => { GLDevice.SetPresentationInterval(presentInterval); }); // End ForceToMainThread } public void SetViewport(Viewport vp) { ForceToMainThread(() => { GLDevice.SetViewport(vp); }); // End ForceToMainThread } public void SetScissorRect(Rectangle scissorRect) { ForceToMainThread(() => { GLDevice.SetScissorRect(scissorRect); }); // End ForceToMainThread } public void SetBlendState(BlendState blendState) { ForceToMainThread(() => { GLDevice.SetBlendState(blendState); }); // End ForceToMainThread } public void SetDepthStencilState(DepthStencilState depthStencilState) { ForceToMainThread(() => { GLDevice.SetDepthStencilState( depthStencilState ); }); // End ForceToMainThread } public void ApplyRasterizerState(RasterizerState rasterizerState) { ForceToMainThread(() => { GLDevice.ApplyRasterizerState(rasterizerState); }); // End ForceToMainThread } public void VerifySampler( int index, Texture texture, SamplerState sampler ) { ForceToMainThread(() => { GLDevice.VerifySampler(index, texture, sampler); }); // End ForceToMainThread } #endregion #region Target Reads/Writes public void Clear( ClearOptions options, Vector4 color, float depth, int stencil ) { ForceToMainThread(() => { GLDevice.Clear(options, color, depth, stencil); }); // End ForceToMainThread } public void SetRenderTargets( RenderTargetBinding[] renderTargets, IGLRenderbuffer renderbuffer, DepthFormat depthFormat ) { ForceToMainThread(() => { GLDevice.SetRenderTargets( renderTargets, renderbuffer, depthFormat ); }); // End ForceToMainThread } public void ResolveTarget(RenderTargetBinding target) { ForceToMainThread(() => { GLDevice.ResolveTarget(target); }); // End ForceToMainThread } public void ReadBackbuffer( IntPtr data, int dataLen, int startIndex, int elementCount, int elementSizeInBytes, int subX, int subY, int subW, int subH ) { ForceToMainThread(() => { GLDevice.ReadBackbuffer( data, dataLen, startIndex, elementCount, elementSizeInBytes, subX, subY, subW, subH ); }); // End ForceToMainThread } #endregion #region Textures public IGLTexture CreateTexture2D( SurfaceFormat format, int width, int height, int levelCount, bool isRenderTarget ) { IGLTexture result = null; ForceToMainThread(() => { result = GLDevice.CreateTexture2D( format, width, height, levelCount, isRenderTarget ); }); // End ForceToMainThread return result; } public IGLTexture CreateTexture3D( SurfaceFormat format, int width, int height, int depth, int levelCount ) { IGLTexture result = null; ForceToMainThread(() => { result = GLDevice.CreateTexture3D( format, width, height, depth, levelCount ); }); // End ForceToMainThread return result; } public IGLTexture CreateTextureCube( SurfaceFormat format, int size, int levelCount, bool isRenderTarget ) { IGLTexture result = null; ForceToMainThread(() => { result = GLDevice.CreateTextureCube( format, size, levelCount, isRenderTarget ); }); // End ForceToMainThread return result; } public void AddDisposeTexture(IGLTexture texture) { GCTextures.Enqueue(texture); } public void SetTextureData2D( IGLTexture texture, SurfaceFormat format, int x, int y, int w, int h, int level, IntPtr data, int dataLength ) { ForceToMainThread(() => { GLDevice.SetTextureData2D( texture, format, x, y, w, h, level, data, dataLength ); }); // End ForceToMainThread } public void SetTextureData3D( IGLTexture texture, SurfaceFormat format, int level, int left, int top, int right, int bottom, int front, int back, IntPtr data, int dataLength ) { ForceToMainThread(() => { GLDevice.SetTextureData3D( texture, format, level, left, top, right, bottom, front, back, data, dataLength ); }); // End ForceToMainThread } public void SetTextureDataCube( IGLTexture texture, SurfaceFormat format, int xOffset, int yOffset, int width, int height, CubeMapFace cubeMapFace, int level, IntPtr data, int dataLength ) { ForceToMainThread(() => { GLDevice.SetTextureDataCube( texture, format, xOffset, yOffset, width, height, cubeMapFace, level, data, dataLength ); }); // End ForceToMainThread } public void SetTextureDataYUV(Texture2D[] textures, IntPtr ptr) { ForceToMainThread(() => { GLDevice.SetTextureDataYUV(textures, ptr); }); // End ForceToMainThread } public void GetTextureData2D( IGLTexture texture, SurfaceFormat format, int width, int height, int level, int subX, int subY, int subW, int subH, IntPtr data, int startIndex, int elementCount, int elementSizeInBytes ) { ForceToMainThread(() => { GLDevice.GetTextureData2D( texture, format, width, height, level, subX, subY, subW, subH, data, startIndex, elementCount, elementSizeInBytes ); }); // End ForceToMainThread } public void GetTextureData3D( IGLTexture texture, SurfaceFormat format, int left, int top, int front, int right, int bottom, int back, int level, IntPtr data, int startIndex, int elementCount, int elementSizeInBytes ) { ForceToMainThread(() => { GLDevice.GetTextureData3D( texture, format, left, top, front, right, bottom, back, level, data, startIndex, elementCount, elementSizeInBytes ); }); // End ForceToMainThread } public void GetTextureDataCube( IGLTexture texture, SurfaceFormat format, int size, CubeMapFace cubeMapFace, int level, int subX, int subY, int subW, int subH, IntPtr data, int startIndex, int elementCount, int elementSizeInBytes ) { ForceToMainThread(() => { GLDevice.GetTextureDataCube( texture, format, size, cubeMapFace, level, subX, subY, subW, subH, data, startIndex, elementCount, elementSizeInBytes ); }); // End ForceToMainThread } #endregion #region Renderbuffers public IGLRenderbuffer GenRenderbuffer( int width, int height, SurfaceFormat format, int multiSampleCount, IGLTexture texture ) { IGLRenderbuffer result = null; ForceToMainThread(() => { result = GLDevice.GenRenderbuffer( width, height, format, multiSampleCount, texture ); }); // End ForceToMainThread return result; } public IGLRenderbuffer GenRenderbuffer( int width, int height, DepthFormat format, int multiSampleCount ) { IGLRenderbuffer result = null; ForceToMainThread(() => { result = GLDevice.GenRenderbuffer( width, height, format, multiSampleCount ); }); // End ForceToMainThread return result; } public void AddDisposeRenderbuffer(IGLRenderbuffer renderbuffer) { GCRenderbuffers.Enqueue(renderbuffer); } #endregion #region Vertex/Index Buffers public IGLBuffer GenVertexBuffer( bool dynamic, BufferUsage usage, int vertexCount, int vertexStride ) { IGLBuffer result = null; ForceToMainThread(() => { result = GLDevice.GenVertexBuffer( dynamic, usage, vertexCount, vertexStride ); }); // End ForceToMainThread return result; } public void AddDisposeVertexBuffer(IGLBuffer buffer) { GCVertexBuffers.Enqueue(buffer); } public void SetVertexBufferData( IGLBuffer buffer, int offsetInBytes, IntPtr data, int dataLength, SetDataOptions options ) { ForceToMainThread(() => { GLDevice.SetVertexBufferData( buffer, offsetInBytes, data, dataLength, options ); }); // End ForceToMainThread } public void GetVertexBufferData( IGLBuffer buffer, int offsetInBytes, IntPtr data, int startIndex, int elementCount, int elementSizeInBytes, int vertexStride ) { ForceToMainThread(() => { GLDevice.GetVertexBufferData( buffer, offsetInBytes, data, startIndex, elementCount, elementSizeInBytes, vertexStride ); }); // End ForceToMainThread } public IGLBuffer GenIndexBuffer( bool dynamic, BufferUsage usage, int indexCount, IndexElementSize indexElementSize ) { IGLBuffer result = null; ForceToMainThread(() => { result = GLDevice.GenIndexBuffer( dynamic, usage, indexCount, indexElementSize ); }); // End ForceToMainThread return result; } public void AddDisposeIndexBuffer(IGLBuffer buffer) { GCIndexBuffers.Enqueue(buffer); } public void SetIndexBufferData( IGLBuffer buffer, int offsetInBytes, IntPtr data, int dataLength, SetDataOptions options ) { ForceToMainThread(() => { GLDevice.SetIndexBufferData( buffer, offsetInBytes, data, dataLength, options ); }); // End ForceToMainThread } public void GetIndexBufferData( IGLBuffer buffer, int offsetInBytes, IntPtr data, int startIndex, int elementCount, int elementSizeInBytes ) { ForceToMainThread(() => { GLDevice.GetIndexBufferData( buffer, offsetInBytes, data, startIndex, elementCount, elementSizeInBytes ); }); // End ForceToMainThread } #endregion #region Effects public IGLEffect CreateEffect(byte[] effectCode) { IGLEffect result = null; ForceToMainThread(() => { result = GLDevice.CreateEffect(effectCode); }); // End ForceToMainThread return result; } public IGLEffect CloneEffect(IGLEffect effect) { IGLEffect result = null; ForceToMainThread(() => { result = GLDevice.CloneEffect(effect); }); // End ForceToMainThread return result; } public void AddDisposeEffect(IGLEffect effect) { GCEffects.Enqueue(effect); } public void ApplyEffect( IGLEffect effect, IntPtr technique, uint pass, IntPtr stateChanges ) { ForceToMainThread(() => { GLDevice.ApplyEffect( effect, technique, pass, stateChanges ); }); // End ForceToMainThread } public void BeginPassRestore(IGLEffect effect, IntPtr stateChanges) { ForceToMainThread(() => { GLDevice.BeginPassRestore(effect, stateChanges); }); // End ForceToMainThread } public void EndPassRestore(IGLEffect effect) { ForceToMainThread(() => { GLDevice.EndPassRestore(effect); }); // End ForceToMainThread } #endregion #region Vertex Attributes public void ApplyVertexAttributes( VertexBufferBinding[] bindings, int numBindings, bool bindingsUpdated, int baseVertex ) { ForceToMainThread(() => { GLDevice.ApplyVertexAttributes( bindings, numBindings, bindingsUpdated, baseVertex ); }); // End ForceToMainThread } public void ApplyVertexAttributes( VertexDeclaration vertexDeclaration, IntPtr ptr, int vertexOffset ) { ForceToMainThread(() => { GLDevice.ApplyVertexAttributes( vertexDeclaration, ptr, vertexOffset ); }); // End ForceToMainThread } #endregion #region Queries public IGLQuery CreateQuery() { IGLQuery result = null; ForceToMainThread(() => { result = GLDevice.CreateQuery(); }); // End ForceToMainThread return result; } public void AddDisposeQuery(IGLQuery query) { GCQueries.Enqueue(query); } public void QueryBegin(IGLQuery query) { ForceToMainThread(() => { GLDevice.QueryBegin(query); }); // End ForceToMainThread } public void QueryEnd(IGLQuery query) { ForceToMainThread(() => { GLDevice.QueryEnd(query); }); // End ForceToMainThread } public bool QueryComplete(IGLQuery query) { bool result = false; ForceToMainThread(() => { result = GLDevice.QueryComplete(query); }); // End ForceToMainThread return result; } public int QueryPixelCount(IGLQuery query) { int result = 0; ForceToMainThread(() => { result = GLDevice.QueryPixelCount(query); }); // End ForceToMainThread return result; } #endregion } }