From 6c338eb06c3bea41bc379af9f664f66855be5f74 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 3 Jun 2018 19:06:26 +0100 Subject: [PATCH] Revert "Run graphics rendering on a dedicated thread." This reverts commit b9be52c5428d4a3862d62fe3a2c01663bd3692c3. --- OpenRA.Game/Graphics/PlatformInterfaces.cs | 2 +- OpenRA.Game/Renderer.cs | 11 +- OpenRA.Platforms.Default/DefaultPlatform.cs | 8 +- OpenRA.Platforms.Default/FrameBuffer.cs | 13 +- .../IGraphicsDeviceInternal.cs | 21 - OpenRA.Platforms.Default/ITextureInternal.cs | 19 - .../OpenRA.Platforms.Default.csproj | 3 - .../Sdl2GraphicsDevice.cs | 33 +- OpenRA.Platforms.Default/Sdl2Input.cs | 2 + OpenRA.Platforms.Default/Shader.cs | 2 +- OpenRA.Platforms.Default/Texture.cs | 9 +- OpenRA.Platforms.Default/ThreadAffine.cs | 9 +- .../ThreadedGraphicsDevice.cs | 784 ------------------ OpenRA.Platforms.Default/VertexBuffer.cs | 7 +- 14 files changed, 48 insertions(+), 875 deletions(-) delete mode 100644 OpenRA.Platforms.Default/IGraphicsDeviceInternal.cs delete mode 100644 OpenRA.Platforms.Default/ITextureInternal.cs delete mode 100644 OpenRA.Platforms.Default/ThreadedGraphicsDevice.cs diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index caddd6fd21..fe1ffcc450 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -17,7 +17,7 @@ namespace OpenRA { public interface IPlatform { - IGraphicsDevice CreateGraphics(Size size, WindowMode windowMode, int batchSize); + IGraphicsDevice CreateGraphics(Size size, WindowMode windowMode); ISoundEngine CreateSound(string device); } diff --git a/OpenRA.Game/Renderer.cs b/OpenRA.Game/Renderer.cs index 07cecadd5b..2d737157fb 100644 --- a/OpenRA.Game/Renderer.cs +++ b/OpenRA.Game/Renderer.cs @@ -51,7 +51,7 @@ namespace OpenRA { var resolution = GetResolution(graphicSettings); - Device = platform.CreateGraphics(new Size(resolution.Width, resolution.Height), graphicSettings.Mode, graphicSettings.BatchSize); + Device = platform.CreateGraphics(new Size(resolution.Width, resolution.Height), graphicSettings.Mode); TempBufferSize = graphicSettings.BatchSize; SheetSize = graphicSettings.SheetSize; @@ -92,11 +92,8 @@ namespace OpenRA Device.OnWindowScaleChanged += (before, after) => { - Game.RunAfterTick(() => - { - foreach (var f in Fonts) - f.Value.SetScale(after); - }); + foreach (var f in Fonts) + f.Value.SetScale(after); }; } @@ -267,6 +264,7 @@ namespace OpenRA public void Dispose() { + Device.Dispose(); WorldModelRenderer.Dispose(); tempBuffer.Dispose(); if (fontSheetBuilder != null) @@ -274,7 +272,6 @@ namespace OpenRA if (Fonts != null) foreach (var font in Fonts.Values) font.Dispose(); - Device.Dispose(); } public string GetClipboardText() diff --git a/OpenRA.Platforms.Default/DefaultPlatform.cs b/OpenRA.Platforms.Default/DefaultPlatform.cs index 42714bffca..9dd432aaf0 100644 --- a/OpenRA.Platforms.Default/DefaultPlatform.cs +++ b/OpenRA.Platforms.Default/DefaultPlatform.cs @@ -10,17 +10,15 @@ #endregion using System.Drawing; +using OpenRA; namespace OpenRA.Platforms.Default { public class DefaultPlatform : IPlatform { - public IGraphicsDevice CreateGraphics(Size size, WindowMode windowMode, int batchSize) + public IGraphicsDevice CreateGraphics(Size size, WindowMode windowMode) { - // Run graphics rendering on a dedicated thread. - // The calling thread will then have more time to process other tasks, since rendering happens in parallel. - // If the calling thread is the main game thread, this means it can run more logic and render ticks. - return new ThreadedGraphicsDevice(new Sdl2GraphicsDevice(size, windowMode), batchSize); + return new Sdl2GraphicsDevice(size, windowMode); } public ISoundEngine CreateSound(string device) diff --git a/OpenRA.Platforms.Default/FrameBuffer.cs b/OpenRA.Platforms.Default/FrameBuffer.cs index 10a5ae86c0..d2218dbb2c 100644 --- a/OpenRA.Platforms.Default/FrameBuffer.cs +++ b/OpenRA.Platforms.Default/FrameBuffer.cs @@ -18,12 +18,12 @@ namespace OpenRA.Platforms.Default { sealed class FrameBuffer : ThreadAffine, IFrameBuffer { - readonly ITexture texture; + readonly Texture texture; readonly Size size; uint framebuffer, depth; bool disposed; - public FrameBuffer(Size size, ITextureInternal texture) + public FrameBuffer(Size size) { this.size = size; if (!Exts.IsPowerOf2(size.Width) || !Exts.IsPowerOf2(size.Height)) @@ -35,7 +35,7 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); // Color - this.texture = texture; + texture = new Texture(); texture.SetEmpty(size.Width, size.Height); OpenGL.glFramebufferTexture2D(OpenGL.FRAMEBUFFER_EXT, OpenGL.COLOR_ATTACHMENT0_EXT, OpenGL.GL_TEXTURE_2D, texture.ID, 0); OpenGL.CheckGLError(); @@ -120,9 +120,14 @@ namespace OpenRA.Platforms.Default } } + ~FrameBuffer() + { + Game.RunAfterTick(() => Dispose(false)); + } + public void Dispose() { - Dispose(true); + Game.RunAfterTick(() => Dispose(true)); GC.SuppressFinalize(this); } diff --git a/OpenRA.Platforms.Default/IGraphicsDeviceInternal.cs b/OpenRA.Platforms.Default/IGraphicsDeviceInternal.cs deleted file mode 100644 index 34bce7ffc7..0000000000 --- a/OpenRA.Platforms.Default/IGraphicsDeviceInternal.cs +++ /dev/null @@ -1,21 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2018 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.Drawing; - -namespace OpenRA.Platforms.Default -{ - interface IGraphicsDeviceInternal : IGraphicsDevice - { - void InitializeOpenGL(); - IFrameBuffer CreateFrameBuffer(Size s, ITextureInternal texture); - } -} diff --git a/OpenRA.Platforms.Default/ITextureInternal.cs b/OpenRA.Platforms.Default/ITextureInternal.cs deleted file mode 100644 index 03ce085a6d..0000000000 --- a/OpenRA.Platforms.Default/ITextureInternal.cs +++ /dev/null @@ -1,19 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2018 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 - -namespace OpenRA.Platforms.Default -{ - interface ITextureInternal : ITexture - { - uint ID { get; } - void SetEmpty(int width, int height); - } -} diff --git a/OpenRA.Platforms.Default/OpenRA.Platforms.Default.csproj b/OpenRA.Platforms.Default/OpenRA.Platforms.Default.csproj index 01502d4fc8..d9dff01017 100644 --- a/OpenRA.Platforms.Default/OpenRA.Platforms.Default.csproj +++ b/OpenRA.Platforms.Default/OpenRA.Platforms.Default.csproj @@ -48,8 +48,6 @@ - - @@ -57,7 +55,6 @@ - diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs index 1fbb47435f..d0edb3c6fc 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs @@ -18,7 +18,7 @@ using SDL2; namespace OpenRA.Platforms.Default { - sealed class Sdl2GraphicsDevice : ThreadAffine, IGraphicsDeviceInternal + sealed class Sdl2GraphicsDevice : ThreadAffine, IGraphicsDevice { readonly Sdl2Input input; @@ -140,14 +140,6 @@ namespace OpenRA.Platforms.Default SDL.SDL_SetHint(SDL.SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); } - SDL.SDL_SetModState(SDL.SDL_Keymod.KMOD_NONE); - input = new Sdl2Input(); - } - - public void InitializeOpenGL() - { - SetThreadAffinity(); - context = SDL.SDL_GL_CreateContext(window); if (context == IntPtr.Zero || SDL.SDL_GL_MakeCurrent(window, context) < 0) throw new InvalidOperationException("Can not create OpenGL context. (Error: {0})".F(SDL.SDL_GetError())); @@ -160,6 +152,9 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); OpenGL.glEnableVertexAttribArray(Shader.TexMetadataAttributeIndex); OpenGL.CheckGLError(); + + SDL.SDL_SetModState(SDL.SDL_Keymod.KMOD_NONE); + input = new Sdl2Input(); } public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot) @@ -226,9 +221,7 @@ namespace OpenRA.Platforms.Default SurfaceSize = new Size(width, height); WindowScale = width * 1f / WindowSize.Width; - var onWindowScaleChanged = OnWindowScaleChanged; - if (onWindowScaleChanged != null) - onWindowScaleChanged(oldScale, WindowScale); + OnWindowScaleChanged(oldScale, WindowScale); } } } @@ -269,9 +262,14 @@ namespace OpenRA.Platforms.Default } } + ~SDL2HardwareCursor() + { + Game.RunAfterTick(() => Dispose(false)); + } + public void Dispose() { - Dispose(true); + Game.RunAfterTick(() => Dispose(true)); GC.SuppressFinalize(this); } @@ -492,7 +490,7 @@ namespace OpenRA.Platforms.Default public void PumpInput(IInputHandler inputHandler) { VerifyThreadAffinity(); - Game.RunAfterTick(() => input.PumpInput(this, inputHandler)); + input.PumpInput(this, inputHandler); } public string GetClipboardText() @@ -526,14 +524,9 @@ namespace OpenRA.Platforms.Default } public IFrameBuffer CreateFrameBuffer(Size s) - { - return CreateFrameBuffer(s, new Texture()); - } - - public IFrameBuffer CreateFrameBuffer(Size s, ITextureInternal texture) { VerifyThreadAffinity(); - return new FrameBuffer(s, texture); + return new FrameBuffer(s); } public IShader CreateShader(string name) diff --git a/OpenRA.Platforms.Default/Sdl2Input.cs b/OpenRA.Platforms.Default/Sdl2Input.cs index f578e1efb3..f135908ebb 100644 --- a/OpenRA.Platforms.Default/Sdl2Input.cs +++ b/OpenRA.Platforms.Default/Sdl2Input.cs @@ -191,6 +191,8 @@ namespace OpenRA.Platforms.Default inputHandler.OnMouseInput(pendingMotion.Value); pendingMotion = null; } + + OpenGL.CheckGLError(); } } } diff --git a/OpenRA.Platforms.Default/Shader.cs b/OpenRA.Platforms.Default/Shader.cs index c357d96f5a..a373054887 100644 --- a/OpenRA.Platforms.Default/Shader.cs +++ b/OpenRA.Platforms.Default/Shader.cs @@ -139,7 +139,7 @@ namespace OpenRA.Platforms.Default foreach (var kv in textures) { OpenGL.glActiveTexture(OpenGL.GL_TEXTURE0 + kv.Key); - OpenGL.glBindTexture(OpenGL.GL_TEXTURE_2D, ((ITextureInternal)kv.Value).ID); + OpenGL.glBindTexture(OpenGL.GL_TEXTURE_2D, ((Texture)kv.Value).ID); } OpenGL.CheckGLError(); diff --git a/OpenRA.Platforms.Default/Texture.cs b/OpenRA.Platforms.Default/Texture.cs index 36a03d307e..8b67ab3f16 100644 --- a/OpenRA.Platforms.Default/Texture.cs +++ b/OpenRA.Platforms.Default/Texture.cs @@ -16,7 +16,7 @@ using System.IO; namespace OpenRA.Platforms.Default { - sealed class Texture : ThreadAffine, ITextureInternal + sealed class Texture : ThreadAffine, ITexture { uint texture; TextureScaleFilter scaleFilter; @@ -187,9 +187,14 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); } + ~Texture() + { + Game.RunAfterTick(() => Dispose(false)); + } + public void Dispose() { - Dispose(true); + Game.RunAfterTick(() => Dispose(true)); GC.SuppressFinalize(this); } diff --git a/OpenRA.Platforms.Default/ThreadAffine.cs b/OpenRA.Platforms.Default/ThreadAffine.cs index f3f5562025..1a9b97e027 100644 --- a/OpenRA.Platforms.Default/ThreadAffine.cs +++ b/OpenRA.Platforms.Default/ThreadAffine.cs @@ -16,14 +16,9 @@ namespace OpenRA.Platforms.Default { abstract class ThreadAffine { - volatile int managedThreadId; + readonly int managedThreadId; protected ThreadAffine() - { - SetThreadAffinity(); - } - - protected void SetThreadAffinity() { managedThreadId = Thread.CurrentThread.ManagedThreadId; } @@ -31,7 +26,7 @@ namespace OpenRA.Platforms.Default protected void VerifyThreadAffinity() { if (managedThreadId != Thread.CurrentThread.ManagedThreadId) - throw new InvalidOperationException("Cross-thread operation not valid: This method must only be called from the thread that owns this object."); + throw new InvalidOperationException("Cross-thread operation not valid: This method must be called from the same thread that created this object."); } } } diff --git a/OpenRA.Platforms.Default/ThreadedGraphicsDevice.cs b/OpenRA.Platforms.Default/ThreadedGraphicsDevice.cs deleted file mode 100644 index b3168901fd..0000000000 --- a/OpenRA.Platforms.Default/ThreadedGraphicsDevice.cs +++ /dev/null @@ -1,784 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2018 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.Drawing; -using System.Runtime.ExceptionServices; -using System.Threading; -using OpenRA.Graphics; - -namespace OpenRA.Platforms.Default -{ - /// - /// 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. - /// - sealed class ThreadedGraphicsDevice : IGraphicsDevice - { - // PERF: Maintain several object pools to reduce allocations. - readonly Stack verticesPool = new Stack(); - readonly Stack messagePool = new Stack(); - readonly Queue messages = new Queue(); - - readonly object syncObject = new object(); - readonly Thread renderThread; - readonly int batchSize; - volatile ExceptionDispatchInfo messageException; - - // Delegates that perform actions on the real device. - Func doClear; - Action doClearDepthBuffer; - Action doDisableDepthBuffer; - Action doEnableDepthBuffer; - Action doDisableScissor; - Action doPresent; - Action doGrabWindowMouseFocus; - Action doReleaseWindowMouseFocus; - Func getWindowSize; - Func getWindowScale; - Func getGLVersion; - Func getCreateTexture; - Func getCreateTextureBitmap; - Func getTakeScreenshot; - Func getGetClipboardText; - Action doSetHardwareCursor; - Func getCreateFrameBuffer; - Func getCreateShader; - Func> getCreateVertexBuffer; - Action doDrawPrimitives; - Action doEnableScissor; - Action doPumpInput; - Action doSetBlendMode; - Func getCreateHardwareCursor; - Func getSetClipboardText; - - public ThreadedGraphicsDevice(IGraphicsDeviceInternal device, int batchSize) - { - this.batchSize = batchSize; - renderThread = new Thread(RenderThread) - { - Name = "ThreadedGraphicsDevice RenderThread", - IsBackground = true - }; - renderThread.SetApartmentState(ApartmentState.STA); - 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(device); - Monitor.Wait(syncObject); - } - } - - void RenderThread(object deviceObject) - { - using (var device = (IGraphicsDeviceInternal)deviceObject) - { - // This lock allows the constructor to block until initialization completes. - lock (syncObject) - { - device.InitializeOpenGL(); - - doClear = () => { device.Clear(); return null; }; - doClearDepthBuffer = () => device.ClearDepthBuffer(); - doDisableDepthBuffer = () => device.DisableDepthBuffer(); - doEnableDepthBuffer = () => device.EnableDepthBuffer(); - doDisableScissor = () => device.DisableScissor(); - doPresent = () => device.Present(); - doGrabWindowMouseFocus = () => device.GrabWindowMouseFocus(); - doReleaseWindowMouseFocus = () => device.ReleaseWindowMouseFocus(); - getWindowSize = () => device.WindowSize; - getWindowScale = () => device.WindowScale; - device.OnWindowScaleChanged += (before, after) => - { - var onWindowScaleChanged = OnWindowScaleChanged; - if (onWindowScaleChanged != null) - System.Threading.Tasks.Task.Run(() => onWindowScaleChanged(before, after)); - }; - getGLVersion = () => device.GLVersion; - getCreateTexture = () => new ThreadedTexture(this, (ITextureInternal)device.CreateTexture()); - getCreateTextureBitmap = bitmap => new ThreadedTexture(this, (ITextureInternal)device.CreateTexture((Bitmap)bitmap)); - getTakeScreenshot = () => device.TakeScreenshot(); - getGetClipboardText = () => device.GetClipboardText(); - doSetHardwareCursor = cursor => { device.SetHardwareCursor((IHardwareCursor)cursor); }; - getCreateFrameBuffer = s => new ThreadedFrameBuffer(this, device.CreateFrameBuffer((Size)s, (ITextureInternal)CreateTexture())); - getCreateShader = name => new ThreadedShader(this, device.CreateShader((string)name)); - getCreateVertexBuffer = length => new ThreadedVertexBuffer(this, device.CreateVertexBuffer((int)length)); - doDrawPrimitives = - tuple => - { - var t = (Tuple)tuple; - device.DrawPrimitives(t.Item1, t.Item2, t.Item3); - }; - doEnableScissor = - tuple => - { - var t = (Tuple)tuple; - device.EnableScissor(t.Item1, t.Item2, t.Item3, t.Item4); - }; - doPumpInput = inputHandler => { device.PumpInput((IInputHandler)inputHandler); }; - doSetBlendMode = mode => { device.SetBlendMode((BlendMode)mode); }; - getCreateHardwareCursor = - tuple => - { - var t = (Tuple)tuple; - return device.CreateHardwareCursor(t.Item1, t.Item2, t.Item3, t.Item4); - }; - getSetClipboardText = text => device.SetClipboardText((string)text); - - 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(ThreadedGraphicsDevice device) - { - this.device = device; - } - - readonly AutoResetEvent completed = new AutoResetEvent(false); - readonly ThreadedGraphicsDevice device; - volatile Action action; - volatile Action actionWithParam; - volatile Func func; - volatile Func funcWithParam; - volatile object param; - volatile object result; - volatile ExceptionDispatchInfo edi; - - public void SetAction(Action method) - { - action = method; - } - - public void SetAction(Action method, object state) - { - actionWithParam = method; - param = state; - } - - public void SetAction(Func method) - { - func = method; - } - - public void SetAction(Func method, object state) - { - funcWithParam = method; - param = state; - } - - public void Execute() - { - var wasSend = action != null || actionWithParam != null; - try - { - if (action != null) - { - action(); - result = null; - action = null; - } - else if (actionWithParam != null) - { - actionWithParam(param); - result = null; - actionWithParam = null; - param = null; - } - else if (func != null) - { - result = func(); - func = null; - } - else - { - result = funcWithParam(param); - funcWithParam = null; - param = null; - } - } - catch (Exception ex) - { - edi = ExceptionDispatchInfo.Capture(ex); - - if (wasSend) - device.messageException = edi; - - result = null; - param = null; - action = null; - actionWithParam = null; - func = null; - funcWithParam = null; - } - - if (wasSend) - { - lock (device.messagePool) - device.messagePool.Push(this); - } - else - { - completed.Set(); - } - } - - public object Result() - { - completed.WaitOne(); - - var localEdi = edi; - edi = null; - var localResult = result; - result = null; - - if (localEdi != null) - localEdi.Throw(); - return localResult; - } - } - - Message GetMessage() - { - lock (messagePool) - if (messagePool.Count > 0) - return messagePool.Pop(); - - return new Message(this); - } - - void QueueMessage(Message message) - { - var exception = messageException; - if (exception != null) - exception.Throw(); - - lock (messages) - { - messages.Enqueue(message); - if (messages.Count == 1) - Monitor.Pulse(messages); - } - } - - object RunMessage(Message message) - { - QueueMessage(message); - var result = message.Result(); - lock (messagePool) - messagePool.Push(message); - return result; - } - - /// - /// Sends a message to the rendering thread. - /// This method blocks until the message is processed, and returns the result. - /// - public T Send(Func method) where T : class - { - if (renderThread == Thread.CurrentThread) - return method(); - - var message = GetMessage(); - message.SetAction(method); - return (T)RunMessage(message); - } - - /// - /// Sends a message to the rendering thread. - /// This method blocks until the message is processed, and returns the result. - /// - public T Send(Func method, object state) where T : class - { - if (renderThread == Thread.CurrentThread) - return method(state); - - var message = GetMessage(); - message.SetAction(method, state); - return (T)RunMessage(message); - } - - /// - /// Posts a message to the rendering thread. - /// This method then returns immediately and does not wait for the message to be processed. - /// - public void Post(Action method) - { - if (renderThread == Thread.CurrentThread) - { - method(); - return; - } - - var message = GetMessage(); - message.SetAction(method); - QueueMessage(message); - } - - /// - /// Posts a message to the rendering thread. - /// This method then returns immediately and does not wait for the message to be processed. - /// - public void Post(Action method, object state) - { - if (renderThread == Thread.CurrentThread) - { - method(state); - return; - } - - var message = GetMessage(); - message.SetAction(method, state); - QueueMessage(message); - } - - public void Dispose() - { - // Use a null message to signal the rendering thread to clean up, then wait for it to complete. - QueueMessage(null); - renderThread.Join(); - } - - public string GLVersion - { - get - { - return Send(getGLVersion); - } - } - - public Size WindowSize - { - get - { - return (Size)Send(getWindowSize); - } - } - - public float WindowScale - { - get - { - return (float)Send(getWindowScale); - } - } - - public event Action OnWindowScaleChanged; - - public void Clear() - { - // We send the clear even though we could just post it. - // This ensures all previous messages have been processed before we return. - // This prevents us from queuing up work faster than it can be processed if rendering is behind. - Send(doClear); - } - - public void ClearDepthBuffer() - { - Post(doClearDepthBuffer); - } - - public IFrameBuffer CreateFrameBuffer(Size s) - { - return Send(getCreateFrameBuffer, s); - } - - public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot) - { - return Send(getCreateHardwareCursor, Tuple.Create(name, size, data, hotspot)); - } - - public IShader CreateShader(string name) - { - return Send(getCreateShader, name); - } - - public ITexture CreateTexture() - { - return Send(getCreateTexture); - } - - public ITexture CreateTexture(Bitmap bitmap) - { - return Send(getCreateTextureBitmap, bitmap); - } - - public IVertexBuffer CreateVertexBuffer(int length) - { - return Send(getCreateVertexBuffer, length); - } - - public void DisableDepthBuffer() - { - Post(doDisableDepthBuffer); - } - - public void DisableScissor() - { - Post(doDisableScissor); - } - - public void DrawPrimitives(PrimitiveType type, int firstVertex, int numVertices) - { - Post(doDrawPrimitives, Tuple.Create(type, firstVertex, numVertices)); - } - - public void EnableDepthBuffer() - { - Post(doEnableDepthBuffer); - } - - public void EnableScissor(int left, int top, int width, int height) - { - Post(doEnableScissor, Tuple.Create(left, top, width, height)); - } - - public string GetClipboardText() - { - return Send(getGetClipboardText); - } - - public void GrabWindowMouseFocus() - { - Post(doGrabWindowMouseFocus); - } - - public void Present() - { - Post(doPresent); - } - - public void PumpInput(IInputHandler inputHandler) - { - Post(doPumpInput, inputHandler); - } - - public void ReleaseWindowMouseFocus() - { - Post(doReleaseWindowMouseFocus); - } - - public void SetBlendMode(BlendMode mode) - { - Post(doSetBlendMode, mode); - } - - public bool SetClipboardText(string text) - { - return (bool)Send(getSetClipboardText, text); - } - - public void SetHardwareCursor(IHardwareCursor cursor) - { - Post(doSetHardwareCursor, cursor); - } - - public Bitmap TakeScreenshot() - { - return Send(getTakeScreenshot); - } - } - - class ThreadedFrameBuffer : IFrameBuffer - { - readonly ThreadedGraphicsDevice device; - readonly Func getTexture; - readonly Action bind; - readonly Action unbind; - readonly Action dispose; - - public ThreadedFrameBuffer(ThreadedGraphicsDevice device, IFrameBuffer frameBuffer) - { - this.device = device; - getTexture = () => frameBuffer.Texture; - bind = frameBuffer.Bind; - unbind = frameBuffer.Unbind; - dispose = frameBuffer.Dispose; - } - - public ITexture Texture - { - get - { - return device.Send(getTexture); - } - } - - public void Bind() - { - device.Post(bind); - } - - public void Unbind() - { - device.Post(unbind); - } - - public void Dispose() - { - device.Post(dispose); - } - } - - class ThreadedVertexBuffer : IVertexBuffer - { - readonly ThreadedGraphicsDevice device; - readonly Action bind; - readonly Action setData1; - readonly Func setData2; - readonly Action dispose; - - public ThreadedVertexBuffer(ThreadedGraphicsDevice device, IVertexBuffer vertexBuffer) - { - this.device = device; - bind = vertexBuffer.Bind; - setData1 = tuple => { var t = (Tuple)tuple; vertexBuffer.SetData(t.Item1, t.Item2); device.ReturnVertices(t.Item1); }; - setData2 = tuple => { var t = (Tuple)tuple; vertexBuffer.SetData(t.Item1, t.Item2, t.Item3); return null; }; - dispose = vertexBuffer.Dispose; - } - - public void Bind() - { - device.Post(bind); - } - - public void SetData(Vertex[] vertices, int length) - { - var buffer = device.GetVertices(length); - Array.Copy(vertices, buffer, length); - device.Post(setData1, Tuple.Create(buffer, length)); - } - - public void SetData(IntPtr data, int start, int length) - { - // We can't return until we are finished with the data, so we must Send here. - device.Send(setData2, Tuple.Create(data, start, length)); - } - - public void SetData(Vertex[] vertices, int start, int length) - { - var buffer = device.GetVertices(length); - Array.Copy(vertices, start, buffer, 0, length); - device.Post(setData1, Tuple.Create(buffer, length)); - } - - public void Dispose() - { - device.Post(dispose); - } - } - - class ThreadedTexture : ITextureInternal - { - readonly ThreadedGraphicsDevice device; - readonly uint id; - readonly Func getScaleFilter; - readonly Action setScaleFilter; - readonly Func getSize; - readonly Action setEmpty; - readonly Func getData; - readonly Func setData1; - readonly Func setData2; - readonly Action setData3; - readonly Action dispose; - - public ThreadedTexture(ThreadedGraphicsDevice device, ITextureInternal texture) - { - this.device = device; - id = texture.ID; - getScaleFilter = () => texture.ScaleFilter; - setScaleFilter = value => texture.ScaleFilter = (TextureScaleFilter)value; - getSize = () => texture.Size; - setEmpty = tuple => { var t = (Tuple)tuple; texture.SetEmpty(t.Item1, t.Item2); }; - getData = () => texture.GetData(); - setData1 = colors => { texture.SetData((uint[,])colors); return null; }; - setData2 = bitmap => { texture.SetData((Bitmap)bitmap); return null; }; - setData3 = tuple => { var t = (Tuple)tuple; texture.SetData(t.Item1, t.Item2, t.Item3); }; - dispose = texture.Dispose; - } - - public uint ID - { - get - { - return id; - } - } - - public TextureScaleFilter ScaleFilter - { - get - { - return (TextureScaleFilter)device.Send(getScaleFilter); - } - - set - { - device.Post(setScaleFilter, value); - } - } - - public Size Size - { - get - { - return (Size)device.Send(getSize); - } - } - - public void SetEmpty(int width, int height) - { - device.Post(setEmpty, Tuple.Create(width, height)); - } - - public byte[] GetData() - { - return device.Send(getData); - } - - public void SetData(uint[,] colors) - { - // We can't return until we are finished with the data, so we must Send here. - device.Send(setData1, colors); - } - - public void SetData(Bitmap bitmap) - { - // We can't return until we are finished with the data, so we must Send here. - device.Send(setData2, bitmap); - } - - public void SetData(byte[] colors, int width, int height) - { - // This creates some garbage for the GC to clean up, - // but allows us post a message instead of blocking the message queue by sending it. - var temp = new byte[colors.Length]; - Array.Copy(colors, temp, temp.Length); - device.Post(setData3, Tuple.Create(temp, width, height)); - } - - public void Dispose() - { - device.Post(dispose); - } - } - - class ThreadedShader : IShader - { - readonly ThreadedGraphicsDevice device; - readonly Action prepareRender; - readonly Action setBool; - readonly Action setMatrix; - readonly Action setTexture; - readonly Action setVec1; - readonly Action setVec2; - readonly Action setVec3; - readonly Action setVec4; - - public ThreadedShader(ThreadedGraphicsDevice device, IShader shader) - { - this.device = device; - prepareRender = shader.PrepareRender; - setBool = tuple => { var t = (Tuple)tuple; shader.SetBool(t.Item1, t.Item2); }; - setMatrix = tuple => { var t = (Tuple)tuple; shader.SetMatrix(t.Item1, t.Item2); }; - setTexture = tuple => { var t = (Tuple)tuple; shader.SetTexture(t.Item1, t.Item2); }; - setVec1 = tuple => { var t = (Tuple)tuple; shader.SetVec(t.Item1, t.Item2); }; - setVec2 = tuple => { var t = (Tuple)tuple; shader.SetVec(t.Item1, t.Item2, t.Item3); }; - setVec3 = tuple => { var t = (Tuple)tuple; shader.SetVec(t.Item1, t.Item2, t.Item3); }; - setVec4 = tuple => { var t = (Tuple)tuple; shader.SetVec(t.Item1, t.Item2, t.Item3, t.Item4); }; - } - - public void PrepareRender() - { - device.Post(prepareRender); - } - - public void SetBool(string name, bool value) - { - device.Post(setBool, Tuple.Create(name, value)); - } - - public void SetMatrix(string param, float[] mtx) - { - device.Post(setMatrix, Tuple.Create(param, mtx)); - } - - public void SetTexture(string param, ITexture texture) - { - device.Post(setTexture, Tuple.Create(param, texture)); - } - - public void SetVec(string name, float x) - { - device.Post(setVec1, Tuple.Create(name, x)); - } - - public void SetVec(string name, float[] vec, int length) - { - device.Post(setVec2, Tuple.Create(name, vec, length)); - } - - public void SetVec(string name, float x, float y) - { - device.Post(setVec3, Tuple.Create(name, x, y)); - } - - public void SetVec(string name, float x, float y, float z) - { - device.Post(setVec4, Tuple.Create(name, x, y, z)); - } - } -} \ No newline at end of file diff --git a/OpenRA.Platforms.Default/VertexBuffer.cs b/OpenRA.Platforms.Default/VertexBuffer.cs index 5acbf5ac0c..7f43077005 100644 --- a/OpenRA.Platforms.Default/VertexBuffer.cs +++ b/OpenRA.Platforms.Default/VertexBuffer.cs @@ -103,9 +103,14 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); } + ~VertexBuffer() + { + Game.RunAfterTick(() => Dispose(false)); + } + public void Dispose() { - Dispose(true); + Game.RunAfterTick(() => Dispose(true)); GC.SuppressFinalize(this); }