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
|
public interface IPlatform
|
||||||
{
|
{
|
||||||
IGraphicsDevice CreateGraphics(Size size, WindowMode windowMode, int batchSize);
|
IGraphicsDevice CreateGraphics(Size size, WindowMode windowMode);
|
||||||
ISoundEngine CreateSound(string device);
|
ISoundEngine CreateSound(string device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
var resolution = GetResolution(graphicSettings);
|
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;
|
TempBufferSize = graphicSettings.BatchSize;
|
||||||
SheetSize = graphicSettings.SheetSize;
|
SheetSize = graphicSettings.SheetSize;
|
||||||
@@ -92,11 +92,8 @@ namespace OpenRA
|
|||||||
|
|
||||||
Device.OnWindowScaleChanged += (before, after) =>
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
Device.Dispose();
|
||||||
WorldModelRenderer.Dispose();
|
WorldModelRenderer.Dispose();
|
||||||
tempBuffer.Dispose();
|
tempBuffer.Dispose();
|
||||||
if (fontSheetBuilder != null)
|
if (fontSheetBuilder != null)
|
||||||
@@ -274,7 +272,6 @@ namespace OpenRA
|
|||||||
if (Fonts != null)
|
if (Fonts != null)
|
||||||
foreach (var font in Fonts.Values)
|
foreach (var font in Fonts.Values)
|
||||||
font.Dispose();
|
font.Dispose();
|
||||||
Device.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetClipboardText()
|
public string GetClipboardText()
|
||||||
|
|||||||
@@ -10,17 +10,15 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using OpenRA;
|
||||||
|
|
||||||
namespace OpenRA.Platforms.Default
|
namespace OpenRA.Platforms.Default
|
||||||
{
|
{
|
||||||
public class DefaultPlatform : IPlatform
|
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.
|
return new Sdl2GraphicsDevice(size, windowMode);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISoundEngine CreateSound(string device)
|
public ISoundEngine CreateSound(string device)
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ namespace OpenRA.Platforms.Default
|
|||||||
{
|
{
|
||||||
sealed class FrameBuffer : ThreadAffine, IFrameBuffer
|
sealed class FrameBuffer : ThreadAffine, IFrameBuffer
|
||||||
{
|
{
|
||||||
readonly ITexture texture;
|
readonly Texture texture;
|
||||||
readonly Size size;
|
readonly Size size;
|
||||||
uint framebuffer, depth;
|
uint framebuffer, depth;
|
||||||
bool disposed;
|
bool disposed;
|
||||||
|
|
||||||
public FrameBuffer(Size size, ITextureInternal texture)
|
public FrameBuffer(Size size)
|
||||||
{
|
{
|
||||||
this.size = size;
|
this.size = size;
|
||||||
if (!Exts.IsPowerOf2(size.Width) || !Exts.IsPowerOf2(size.Height))
|
if (!Exts.IsPowerOf2(size.Width) || !Exts.IsPowerOf2(size.Height))
|
||||||
@@ -35,7 +35,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
OpenGL.CheckGLError();
|
OpenGL.CheckGLError();
|
||||||
|
|
||||||
// Color
|
// Color
|
||||||
this.texture = texture;
|
texture = new Texture();
|
||||||
texture.SetEmpty(size.Width, size.Height);
|
texture.SetEmpty(size.Width, size.Height);
|
||||||
OpenGL.glFramebufferTexture2D(OpenGL.FRAMEBUFFER_EXT, OpenGL.COLOR_ATTACHMENT0_EXT, OpenGL.GL_TEXTURE_2D, texture.ID, 0);
|
OpenGL.glFramebufferTexture2D(OpenGL.FRAMEBUFFER_EXT, OpenGL.COLOR_ATTACHMENT0_EXT, OpenGL.GL_TEXTURE_2D, texture.ID, 0);
|
||||||
OpenGL.CheckGLError();
|
OpenGL.CheckGLError();
|
||||||
@@ -120,9 +120,14 @@ namespace OpenRA.Platforms.Default
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~FrameBuffer()
|
||||||
|
{
|
||||||
|
Game.RunAfterTick(() => Dispose(false));
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Game.RunAfterTick(() => Dispose(true));
|
||||||
GC.SuppressFinalize(this);
|
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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="DefaultPlatform.cs" />
|
<Compile Include="DefaultPlatform.cs" />
|
||||||
<Compile Include="IGraphicsDeviceInternal.cs" />
|
|
||||||
<Compile Include="ITextureInternal.cs" />
|
|
||||||
<Compile Include="Sdl2GraphicsDevice.cs" />
|
<Compile Include="Sdl2GraphicsDevice.cs" />
|
||||||
<Compile Include="Sdl2Input.cs" />
|
<Compile Include="Sdl2Input.cs" />
|
||||||
<Compile Include="Shader.cs" />
|
<Compile Include="Shader.cs" />
|
||||||
@@ -57,7 +55,6 @@
|
|||||||
<Compile Include="MultiTapDetection.cs" />
|
<Compile Include="MultiTapDetection.cs" />
|
||||||
<Compile Include="Texture.cs" />
|
<Compile Include="Texture.cs" />
|
||||||
<Compile Include="ThreadAffine.cs" />
|
<Compile Include="ThreadAffine.cs" />
|
||||||
<Compile Include="ThreadedGraphicsDevice.cs" />
|
|
||||||
<Compile Include="VertexBuffer.cs" />
|
<Compile Include="VertexBuffer.cs" />
|
||||||
<Compile Include="OpenAlSoundEngine.cs" />
|
<Compile Include="OpenAlSoundEngine.cs" />
|
||||||
<Compile Include="OpenGL.cs" />
|
<Compile Include="OpenGL.cs" />
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ using SDL2;
|
|||||||
|
|
||||||
namespace OpenRA.Platforms.Default
|
namespace OpenRA.Platforms.Default
|
||||||
{
|
{
|
||||||
sealed class Sdl2GraphicsDevice : ThreadAffine, IGraphicsDeviceInternal
|
sealed class Sdl2GraphicsDevice : ThreadAffine, IGraphicsDevice
|
||||||
{
|
{
|
||||||
readonly Sdl2Input input;
|
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_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);
|
context = SDL.SDL_GL_CreateContext(window);
|
||||||
if (context == IntPtr.Zero || SDL.SDL_GL_MakeCurrent(window, context) < 0)
|
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()));
|
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.CheckGLError();
|
||||||
OpenGL.glEnableVertexAttribArray(Shader.TexMetadataAttributeIndex);
|
OpenGL.glEnableVertexAttribArray(Shader.TexMetadataAttributeIndex);
|
||||||
OpenGL.CheckGLError();
|
OpenGL.CheckGLError();
|
||||||
|
|
||||||
|
SDL.SDL_SetModState(SDL.SDL_Keymod.KMOD_NONE);
|
||||||
|
input = new Sdl2Input();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot)
|
public IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot)
|
||||||
@@ -226,9 +221,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
SurfaceSize = new Size(width, height);
|
SurfaceSize = new Size(width, height);
|
||||||
WindowScale = width * 1f / WindowSize.Width;
|
WindowScale = width * 1f / WindowSize.Width;
|
||||||
|
|
||||||
var onWindowScaleChanged = OnWindowScaleChanged;
|
OnWindowScaleChanged(oldScale, WindowScale);
|
||||||
if (onWindowScaleChanged != null)
|
|
||||||
onWindowScaleChanged(oldScale, WindowScale);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,9 +262,14 @@ namespace OpenRA.Platforms.Default
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~SDL2HardwareCursor()
|
||||||
|
{
|
||||||
|
Game.RunAfterTick(() => Dispose(false));
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Game.RunAfterTick(() => Dispose(true));
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,7 +490,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
public void PumpInput(IInputHandler inputHandler)
|
public void PumpInput(IInputHandler inputHandler)
|
||||||
{
|
{
|
||||||
VerifyThreadAffinity();
|
VerifyThreadAffinity();
|
||||||
Game.RunAfterTick(() => input.PumpInput(this, inputHandler));
|
input.PumpInput(this, inputHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetClipboardText()
|
public string GetClipboardText()
|
||||||
@@ -526,14 +524,9 @@ namespace OpenRA.Platforms.Default
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IFrameBuffer CreateFrameBuffer(Size s)
|
public IFrameBuffer CreateFrameBuffer(Size s)
|
||||||
{
|
|
||||||
return CreateFrameBuffer(s, new Texture());
|
|
||||||
}
|
|
||||||
|
|
||||||
public IFrameBuffer CreateFrameBuffer(Size s, ITextureInternal texture)
|
|
||||||
{
|
{
|
||||||
VerifyThreadAffinity();
|
VerifyThreadAffinity();
|
||||||
return new FrameBuffer(s, texture);
|
return new FrameBuffer(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IShader CreateShader(string name)
|
public IShader CreateShader(string name)
|
||||||
|
|||||||
@@ -191,6 +191,8 @@ namespace OpenRA.Platforms.Default
|
|||||||
inputHandler.OnMouseInput(pendingMotion.Value);
|
inputHandler.OnMouseInput(pendingMotion.Value);
|
||||||
pendingMotion = null;
|
pendingMotion = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OpenGL.CheckGLError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
foreach (var kv in textures)
|
foreach (var kv in textures)
|
||||||
{
|
{
|
||||||
OpenGL.glActiveTexture(OpenGL.GL_TEXTURE0 + kv.Key);
|
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();
|
OpenGL.CheckGLError();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ using System.IO;
|
|||||||
|
|
||||||
namespace OpenRA.Platforms.Default
|
namespace OpenRA.Platforms.Default
|
||||||
{
|
{
|
||||||
sealed class Texture : ThreadAffine, ITextureInternal
|
sealed class Texture : ThreadAffine, ITexture
|
||||||
{
|
{
|
||||||
uint texture;
|
uint texture;
|
||||||
TextureScaleFilter scaleFilter;
|
TextureScaleFilter scaleFilter;
|
||||||
@@ -187,9 +187,14 @@ namespace OpenRA.Platforms.Default
|
|||||||
OpenGL.CheckGLError();
|
OpenGL.CheckGLError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~Texture()
|
||||||
|
{
|
||||||
|
Game.RunAfterTick(() => Dispose(false));
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Game.RunAfterTick(() => Dispose(true));
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,9 @@ namespace OpenRA.Platforms.Default
|
|||||||
{
|
{
|
||||||
abstract class ThreadAffine
|
abstract class ThreadAffine
|
||||||
{
|
{
|
||||||
volatile int managedThreadId;
|
readonly int managedThreadId;
|
||||||
|
|
||||||
protected ThreadAffine()
|
protected ThreadAffine()
|
||||||
{
|
|
||||||
SetThreadAffinity();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void SetThreadAffinity()
|
|
||||||
{
|
{
|
||||||
managedThreadId = Thread.CurrentThread.ManagedThreadId;
|
managedThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||||
}
|
}
|
||||||
@@ -31,7 +26,7 @@ namespace OpenRA.Platforms.Default
|
|||||||
protected void VerifyThreadAffinity()
|
protected void VerifyThreadAffinity()
|
||||||
{
|
{
|
||||||
if (managedThreadId != Thread.CurrentThread.ManagedThreadId)
|
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();
|
OpenGL.CheckGLError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~VertexBuffer()
|
||||||
|
{
|
||||||
|
Game.RunAfterTick(() => Dispose(false));
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Game.RunAfterTick(() => Dispose(true));
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user