Revert "Run graphics rendering on a dedicated thread."
This reverts commit b9be52c5428d4a3862d62fe3a2c01663bd3692c3.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -91,12 +91,9 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
Device.OnWindowScaleChanged += (before, after) =>
|
||||
{
|
||||
Game.RunAfterTick(() =>
|
||||
{
|
||||
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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -48,8 +48,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DefaultPlatform.cs" />
|
||||
<Compile Include="IGraphicsDeviceInternal.cs" />
|
||||
<Compile Include="ITextureInternal.cs" />
|
||||
<Compile Include="Sdl2GraphicsDevice.cs" />
|
||||
<Compile Include="Sdl2Input.cs" />
|
||||
<Compile Include="Shader.cs" />
|
||||
@@ -57,7 +55,6 @@
|
||||
<Compile Include="MultiTapDetection.cs" />
|
||||
<Compile Include="Texture.cs" />
|
||||
<Compile Include="ThreadAffine.cs" />
|
||||
<Compile Include="ThreadedGraphicsDevice.cs" />
|
||||
<Compile Include="VertexBuffer.cs" />
|
||||
<Compile Include="OpenAlSoundEngine.cs" />
|
||||
<Compile Include="OpenGL.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)
|
||||
|
||||
@@ -191,6 +191,8 @@ namespace OpenRA.Platforms.Default
|
||||
inputHandler.OnMouseInput(pendingMotion.Value);
|
||||
pendingMotion = null;
|
||||
}
|
||||
|
||||
OpenGL.CheckGLError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <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 ThreadedGraphicsDevice : IGraphicsDevice
|
||||
{
|
||||
// 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>();
|
||||
|
||||
readonly object syncObject = new object();
|
||||
readonly Thread renderThread;
|
||||
readonly int batchSize;
|
||||
volatile ExceptionDispatchInfo messageException;
|
||||
|
||||
// Delegates that perform actions on the real device.
|
||||
Func<object> doClear;
|
||||
Action doClearDepthBuffer;
|
||||
Action doDisableDepthBuffer;
|
||||
Action doEnableDepthBuffer;
|
||||
Action doDisableScissor;
|
||||
Action doPresent;
|
||||
Action doGrabWindowMouseFocus;
|
||||
Action doReleaseWindowMouseFocus;
|
||||
Func<object> getWindowSize;
|
||||
Func<object> getWindowScale;
|
||||
Func<string> getGLVersion;
|
||||
Func<ITexture> getCreateTexture;
|
||||
Func<object, ITexture> getCreateTextureBitmap;
|
||||
Func<Bitmap> getTakeScreenshot;
|
||||
Func<string> getGetClipboardText;
|
||||
Action<object> doSetHardwareCursor;
|
||||
Func<object, IFrameBuffer> getCreateFrameBuffer;
|
||||
Func<object, IShader> getCreateShader;
|
||||
Func<object, IVertexBuffer<Vertex>> getCreateVertexBuffer;
|
||||
Action<object> doDrawPrimitives;
|
||||
Action<object> doEnableScissor;
|
||||
Action<object> doPumpInput;
|
||||
Action<object> doSetBlendMode;
|
||||
Func<object, IHardwareCursor> getCreateHardwareCursor;
|
||||
Func<object, object> 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<PrimitiveType, int, int>)tuple;
|
||||
device.DrawPrimitives(t.Item1, t.Item2, t.Item3);
|
||||
};
|
||||
doEnableScissor =
|
||||
tuple =>
|
||||
{
|
||||
var t = (Tuple<int, int, int, int>)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<string, Size, byte[], int2>)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<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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message to the rendering thread.
|
||||
/// This method blocks until the message is processed, and returns the result.
|
||||
/// </summary>
|
||||
public T Send<T>(Func<T> method) where T : class
|
||||
{
|
||||
if (renderThread == Thread.CurrentThread)
|
||||
return method();
|
||||
|
||||
var message = GetMessage();
|
||||
message.SetAction(method);
|
||||
return (T)RunMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message to the rendering thread.
|
||||
/// This method blocks until the message is processed, and returns the result.
|
||||
/// </summary>
|
||||
public T Send<T>(Func<object, T> method, object state) where T : class
|
||||
{
|
||||
if (renderThread == Thread.CurrentThread)
|
||||
return method(state);
|
||||
|
||||
var message = GetMessage();
|
||||
message.SetAction(method, state);
|
||||
return (T)RunMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts a message to the rendering thread.
|
||||
/// This method then returns immediately and does not wait for the message to be processed.
|
||||
/// </summary>
|
||||
public void Post(Action method)
|
||||
{
|
||||
if (renderThread == Thread.CurrentThread)
|
||||
{
|
||||
method();
|
||||
return;
|
||||
}
|
||||
|
||||
var message = GetMessage();
|
||||
message.SetAction(method);
|
||||
QueueMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts a message to the rendering thread.
|
||||
/// This method then returns immediately and does not wait for the message to be processed.
|
||||
/// </summary>
|
||||
public void Post(Action<object> 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<float, float> 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<Vertex> 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<ITexture> 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<Vertex>
|
||||
{
|
||||
readonly ThreadedGraphicsDevice device;
|
||||
readonly Action bind;
|
||||
readonly Action<object> setData1;
|
||||
readonly Func<object, object> setData2;
|
||||
readonly Action dispose;
|
||||
|
||||
public ThreadedVertexBuffer(ThreadedGraphicsDevice device, IVertexBuffer<Vertex> vertexBuffer)
|
||||
{
|
||||
this.device = device;
|
||||
bind = vertexBuffer.Bind;
|
||||
setData1 = tuple => { var t = (Tuple<Vertex[], int>)tuple; vertexBuffer.SetData(t.Item1, t.Item2); device.ReturnVertices(t.Item1); };
|
||||
setData2 = tuple => { var t = (Tuple<IntPtr, int, int>)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<object> getScaleFilter;
|
||||
readonly Action<object> setScaleFilter;
|
||||
readonly Func<object> getSize;
|
||||
readonly Action<object> setEmpty;
|
||||
readonly Func<byte[]> getData;
|
||||
readonly Func<object, object> setData1;
|
||||
readonly Func<object, object> setData2;
|
||||
readonly Action<object> 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<int, int>)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<byte[], int, int>)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<object> setBool;
|
||||
readonly Action<object> setMatrix;
|
||||
readonly Action<object> setTexture;
|
||||
readonly Action<object> setVec1;
|
||||
readonly Action<object> setVec2;
|
||||
readonly Action<object> setVec3;
|
||||
readonly Action<object> setVec4;
|
||||
|
||||
public ThreadedShader(ThreadedGraphicsDevice device, IShader shader)
|
||||
{
|
||||
this.device = device;
|
||||
prepareRender = shader.PrepareRender;
|
||||
setBool = tuple => { var t = (Tuple<string, bool>)tuple; shader.SetBool(t.Item1, t.Item2); };
|
||||
setMatrix = tuple => { var t = (Tuple<string, float[]>)tuple; shader.SetMatrix(t.Item1, t.Item2); };
|
||||
setTexture = tuple => { var t = (Tuple<string, ITexture>)tuple; shader.SetTexture(t.Item1, t.Item2); };
|
||||
setVec1 = tuple => { var t = (Tuple<string, float>)tuple; shader.SetVec(t.Item1, t.Item2); };
|
||||
setVec2 = tuple => { var t = (Tuple<string, float[], int>)tuple; shader.SetVec(t.Item1, t.Item2, t.Item3); };
|
||||
setVec3 = tuple => { var t = (Tuple<string, float, float>)tuple; shader.SetVec(t.Item1, t.Item2, t.Item3); };
|
||||
setVec4 = tuple => { var t = (Tuple<string, float, float, float>)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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user