Skip to content

Commit 87cb853

Browse files
committed
Change: Attempt to improve threading for VAO generation
1 parent f05652e commit 87cb853

File tree

11 files changed

+107
-60
lines changed

11 files changed

+107
-60
lines changed

source/LibRender2/Backgrounds/Background.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ private void RenderBackgroundObject(BackgroundObject data)
319319

320320
if (data.Object.Mesh.VAO == null)
321321
{
322-
VAOExtensions.CreateVAO(ref data.Object.Mesh, false, renderer.DefaultShader.VertexLayout, renderer);
322+
VAOExtensions.CreateVAO(data.Object.Mesh, false, renderer.DefaultShader.VertexLayout, renderer);
323323
}
324324
}
325325

source/LibRender2/BaseRenderer.cs

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Diagnostics;
45
using System.IO;
@@ -76,6 +77,8 @@ public abstract class BaseRenderer
7677
/// <summary>The track follower for the main camera</summary>
7778
public TrackFollower CameraTrackFollower;
7879

80+
public bool RenderThreadJobWaiting;
81+
7982
/// <summary>Holds a reference to the current interface type of the game (Used by the renderer)</summary>
8083
public InterfaceType CurrentInterface
8184
{
@@ -687,15 +690,15 @@ public void InitializeVisibility()
687690
{
688691
for (int i = 0; i < StaticObjectStates.Count; i++)
689692
{
690-
VAOExtensions.CreateVAO(ref StaticObjectStates[i].Prototype.Mesh, false, DefaultShader.VertexLayout, this);
693+
VAOExtensions.CreateVAO(StaticObjectStates[i].Prototype.Mesh, false, DefaultShader.VertexLayout, this);
691694
if (StaticObjectStates[i].Matricies != null)
692695
{
693696
GL.CreateBuffers(1, out StaticObjectStates[i].MatrixBufferIndex);
694697
}
695698
}
696699
for (int i = 0; i < DynamicObjectStates.Count; i++)
697700
{
698-
VAOExtensions.CreateVAO(ref DynamicObjectStates[i].Prototype.Mesh, false, DefaultShader.VertexLayout, this);
701+
VAOExtensions.CreateVAO(DynamicObjectStates[i].Prototype.Mesh, false, DefaultShader.VertexLayout, this);
699702
if (DynamicObjectStates[i].Matricies != null)
700703
{
701704
GL.CreateBuffers(1, out DynamicObjectStates[i].MatrixBufferIndex);
@@ -1762,5 +1765,23 @@ public void SetWindowSize(int width, int height)
17621765
SetWindowState(WindowState.Maximized);
17631766
}
17641767
}
1768+
1769+
public ConcurrentQueue<ThreadStart> RenderThreadJobs;
1770+
1771+
/// <summary>This method is used during loading to run commands requiring an OpenGL context in the main render loop</summary>
1772+
/// <param name="job">The OpenGL command</param>
1773+
/// <param name="timeout">The timeout</param>
1774+
public void RunInRenderThread(ThreadStart job, int timeout)
1775+
{
1776+
RenderThreadJobs.Enqueue(job);
1777+
//Don't set the job to available until after it's been loaded into the queue
1778+
RenderThreadJobWaiting = true;
1779+
//Failsafe: If our job has taken more than the timeout, stop waiting for it
1780+
//A missing texture is probably better than an infinite loadscreen
1781+
lock (job)
1782+
{
1783+
Monitor.Wait(job, timeout);
1784+
}
1785+
}
17651786
}
17661787
}

source/LibRender2/Objects/FaceState.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using OpenBveApi.Objects;
2+
using System.Threading;
23

34
namespace LibRender2.Objects
45
{
@@ -19,7 +20,7 @@ public FaceState(ObjectState _object, MeshFace face, BaseRenderer renderer)
1920
Renderer = renderer;
2021
if (Object.Prototype.Mesh.VAO == null && !Renderer.ForceLegacyOpenGL)
2122
{
22-
VAOExtensions.CreateVAO(ref Object.Prototype.Mesh, Object.Prototype.Dynamic, Renderer.DefaultShader.VertexLayout, Renderer);
23+
VAOExtensions.CreateVAO(Object.Prototype.Mesh, Object.Prototype.Dynamic, Renderer.DefaultShader.VertexLayout, Renderer);
2324
}
2425
}
2526

source/LibRender2/openGL/VertexArrayObject.cs

+17-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,23 @@ public static class VAOExtensions
167167
/// <param name="isDynamic">Whether the mesh is dynamic (e.g. part of an animated object / train)</param>
168168
/// <param name="vertexLayout">The vertex layout to use</param>
169169
/// <param name="renderer">A reference to the base renderer</param>
170-
public static void CreateVAO(ref Mesh mesh, bool isDynamic, VertexLayout vertexLayout, BaseRenderer renderer)
170+
public static void CreateVAO(Mesh mesh, bool isDynamic, VertexLayout vertexLayout, BaseRenderer renderer)
171+
{
172+
if (!renderer.GameWindow.Context.IsCurrent)
173+
{
174+
renderer.RunInRenderThread(() =>
175+
{
176+
createVAO(mesh, isDynamic, vertexLayout, renderer);
177+
}, 2000);
178+
}
179+
else
180+
{
181+
createVAO(mesh, isDynamic, vertexLayout, renderer);
182+
}
183+
}
184+
185+
186+
private static void createVAO(Mesh mesh, bool isDynamic, VertexLayout vertexLayout, BaseRenderer renderer)
171187
{
172188
try
173189
{

source/ObjectViewer/System/GameWindow.cs

+14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.ComponentModel;
3+
using System.Threading;
34
using ObjectViewer.Trains;
45
using OpenBveApi;
56
using OpenTK;
@@ -37,6 +38,19 @@ public ObjectViewer(int width, int height, GraphicsMode currentGraphicsMode, str
3738

3839
protected override void OnRenderFrame(FrameEventArgs e)
3940
{
41+
if (Program.Renderer.RenderThreadJobWaiting)
42+
{
43+
while (!Program.Renderer.RenderThreadJobs.IsEmpty)
44+
{
45+
Program.Renderer.RenderThreadJobs.TryDequeue(out ThreadStart currentJob);
46+
currentJob();
47+
lock (currentJob)
48+
{
49+
Monitor.Pulse(currentJob);
50+
}
51+
}
52+
Program.Renderer.RenderThreadJobWaiting = false;
53+
}
4054
double timeElapsed = RenderRealTimeElapsed;
4155

4256
// Use the OpenTK frame rate as this is much more accurate

source/OpenBVE/Graphics/Renderers/Touch.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private void ShowObject(ObjectState state)
6060

6161
if (renderer.AvailableNewRenderer && state.Prototype.Mesh.VAO == null)
6262
{
63-
VAOExtensions.CreateVAO(ref state.Prototype.Mesh, state.Prototype.Dynamic, renderer.pickingShader.VertexLayout, renderer);
63+
VAOExtensions.CreateVAO(state.Prototype.Mesh, state.Prototype.Dynamic, renderer.pickingShader.VertexLayout, renderer);
6464
}
6565
}
6666

source/OpenBVE/System/GameWindow.cs

+26-22
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,21 @@ protected override void OnRenderFrame(FrameEventArgs e)
9999
//If the load is not complete, then we shouldn't be running the mainloop
100100
return;
101101
}
102+
103+
if (Program.Renderer.RenderThreadJobWaiting)
104+
{
105+
while (!Program.Renderer.RenderThreadJobs.IsEmpty)
106+
{
107+
Program.Renderer.RenderThreadJobs.TryDequeue(out ThreadStart currentJob);
108+
currentJob();
109+
lock (currentJob)
110+
{
111+
Monitor.Pulse(currentJob);
112+
}
113+
}
114+
}
115+
116+
Program.Renderer.RenderThreadJobWaiting = false;
102117
double TimeElapsed = RenderTimeElapsed;
103118
double RealTimeElapsed = RenderRealTimeElapsed;
104119

@@ -395,7 +410,7 @@ protected override void OnLoad(EventArgs e)
395410
}
396411
Program.FileSystem.AppendToLogFile("Game window initialised successfully.");
397412
//Initialise the loader thread queues
398-
jobs = new ConcurrentQueue<ThreadStart>();
413+
Program.Renderer.RenderThreadJobs = new ConcurrentQueue<ThreadStart>();
399414
Program.Renderer.Initialize();
400415
Program.Renderer.DetermineMaxAFLevel();
401416
Interface.CurrentOptions.Save(OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "1.5.0/options.cfg"));
@@ -1078,16 +1093,21 @@ private void SetupSimulation()
10781093
}
10791094
}
10801095
}
1096+
1097+
simulationSetup = true;
10811098
}
10821099

1100+
private Thread setupThread;
1101+
private bool simulationSetup = false;
1102+
10831103
public void LoadingScreenLoop()
10841104
{
10851105
Program.Renderer.PushMatrix(MatrixMode.Projection);
10861106
Matrix4D.CreateOrthographicOffCenter(0.0f, Program.Renderer.Screen.Width, Program.Renderer.Screen.Height, 0.0f, -1.0f, 1.0f, out Program.Renderer.CurrentProjectionMatrix);
10871107
Program.Renderer.PushMatrix(MatrixMode.Modelview);
10881108
Program.Renderer.CurrentViewMatrix = Matrix4D.Identity;
10891109

1090-
while (!Loading.Complete && !Loading.Cancel)
1110+
while (!Loading.Complete && !Loading.Cancel && !simulationSetup)
10911111
{
10921112
CPreciseTimer.GetElapsedTime();
10931113
this.ProcessEvents();
@@ -1124,19 +1144,19 @@ public void LoadingScreenLoop()
11241144
Program.Renderer.Loading.DrawLoadingScreen(Program.Renderer.Fonts.SmallFont, routeProgress, finalTrainProgress);
11251145
SwapBuffers();
11261146

1127-
if (Loading.JobAvailable)
1147+
if (Program.Renderer.RenderThreadJobWaiting)
11281148
{
1129-
while (!jobs.IsEmpty)
1149+
while (!Program.Renderer.RenderThreadJobs.IsEmpty)
11301150
{
1131-
jobs.TryDequeue(out ThreadStart currentJob);
1151+
Program.Renderer.RenderThreadJobs.TryDequeue(out ThreadStart currentJob);
11321152
currentJob();
11331153
lock (currentJob)
11341154
{
11351155
Monitor.Pulse(currentJob);
11361156
}
11371157

11381158
}
1139-
Loading.JobAvailable = false;
1159+
Program.Renderer.RenderThreadJobWaiting = false;
11401160
}
11411161
double time = CPreciseTimer.GetElapsedTime();
11421162
double wait = 1000.0 / 60.0 - time * 1000 - 50;
@@ -1153,22 +1173,6 @@ public void LoadingScreenLoop()
11531173
}
11541174
}
11551175

1156-
private static ConcurrentQueue<ThreadStart> jobs;
11571176

1158-
/// <summary>This method is used during loading to run commands requiring an OpenGL context in the main render loop</summary>
1159-
/// <param name="job">The OpenGL command</param>
1160-
/// <param name="timeout">The timeout</param>
1161-
internal static void RunInRenderThread(ThreadStart job, int timeout)
1162-
{
1163-
jobs.Enqueue(job);
1164-
//Don't set the job to available until after it's been loaded into the queue
1165-
Loading.JobAvailable = true;
1166-
//Failsafe: If our job has taken more than the timeout, stop waiting for it
1167-
//A missing texture is probably better than an infinite loadscreen
1168-
lock (job)
1169-
{
1170-
Monitor.Wait(job, timeout);
1171-
}
1172-
}
11731177
}
11741178
}

source/OpenBVE/System/Host.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ public override bool RegisterTexture(string path, TextureParameters parameters,
214214
handle = data;
215215
if (loadTexture)
216216
{
217-
OpenBVEGame.RunInRenderThread(() =>
217+
Program.Renderer.RunInRenderThread(() =>
218218
{
219219
LoadTexture(ref data, OpenGlTextureWrapMode.ClampClamp);
220220
}, timeout);

source/OpenBVE/System/Loading.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ internal static bool Cancel
5151
internal static bool Complete;
5252
/// <summary>True when the simulation has been completely setup</summary>
5353
internal static bool SimulationSetup;
54-
/// <summary>Whether there is currently a job waiting to complete in the main game loop</summary>
55-
internal static bool JobAvailable = false;
5654
private static Thread Loader;
5755
/// <summary>The current route file</summary>
5856
private static string CurrentRouteFile;
@@ -306,7 +304,7 @@ private static void LoadThreaded() {
306304
Program.RestartArguments = " ";
307305
Cancel = true;
308306
}
309-
if (JobAvailable)
307+
if (Program.Renderer.RenderThreadJobWaiting)
310308
{
311309
Thread.Sleep(10);
312310
}

source/RouteViewer/System/Gamewindow.cs

+19-26
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,20 @@ protected override void OnRenderFrame(FrameEventArgs e)
6161
{
6262
return;
6363
}
64-
ProcessEvents();
64+
if (Program.Renderer.RenderThreadJobWaiting)
65+
{
66+
while (!Program.Renderer.RenderThreadJobs.IsEmpty)
67+
{
68+
Program.Renderer.RenderThreadJobs.TryDequeue(out ThreadStart currentJob);
69+
currentJob();
70+
lock (currentJob)
71+
{
72+
Monitor.Pulse(currentJob);
73+
}
74+
}
75+
Program.Renderer.RenderThreadJobWaiting = false;
76+
}
77+
ProcessEvents();
6578
double TimeElapsed = CPreciseTimer.GetElapsedTime();
6679

6780
if (Program.CurrentRouteFile != null)
@@ -164,18 +177,19 @@ public static void LoadingScreenLoop()
164177
Program.Renderer.Loading.DrawLoadingScreen(Program.Renderer.Fonts.SmallFont, routeProgress);
165178
Program.Renderer.GameWindow.SwapBuffers();
166179

167-
if (Loading.JobAvailable)
180+
if (Program.Renderer.RenderThreadJobWaiting)
168181
{
169-
while (jobs.Count > 0)
182+
while (!Program.Renderer.RenderThreadJobs.IsEmpty)
170183
{
171-
jobs.TryDequeue(out ThreadStart currentJob);
184+
Program.Renderer.RenderThreadJobs.TryDequeue(out ThreadStart currentJob);
172185
currentJob();
173186
lock (currentJob)
174187
{
175188
Monitor.Pulse(currentJob);
176189
}
190+
177191
}
178-
Loading.JobAvailable = false;
192+
Program.Renderer.RenderThreadJobWaiting = false;
179193
}
180194
double time = CPreciseTimer.GetElapsedTime();
181195
double wait = 1000.0 / 60.0 - time * 1000 - 50;
@@ -195,26 +209,5 @@ public static void LoadingScreenLoop()
195209
Program.CurrentRouteFile = null;
196210
}
197211
}
198-
199-
#pragma warning disable 0649
200-
private static ConcurrentQueue<ThreadStart> jobs;
201-
#pragma warning restore 0649
202-
203-
/// <summary>This method is used during loading to run commands requiring an OpenGL context in the main render loop</summary>
204-
/// <param name="job">The OpenGL command</param>
205-
/// <param name="timeout">The timeout</param>
206-
internal static void RunInRenderThread(ThreadStart job, int timeout)
207-
{
208-
object locker = new object();
209-
jobs.Enqueue(job);
210-
//Don't set the job to available until after it's been loaded into the queue
211-
Loading.JobAvailable = true;
212-
//Failsafe: If our job has taken more than the timeout, stop waiting for it
213-
//A missing texture is probably better than an infinite loadscreen
214-
lock (job)
215-
{
216-
Monitor.Wait(job, timeout);
217-
}
218-
}
219212
}
220213
}

source/TrainManager/Car/CarBase.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,7 @@ private void UpdateCarSectionElement(int SectionIndex, int GroupIndex, int Eleme
784784
CarSections[SectionIndex].Groups[GroupIndex].Elements[ElementIndex].Update(baseTrain, Index, FrontAxle.Follower.TrackPosition - FrontAxle.Position, p, Direction, Up, Side, updatefunctions, Show, timeDelta, EnableDamping, false, CarSections[SectionIndex].Type == ObjectType.Overlay ? TrainManagerBase.Renderer.Camera : null);
785785
if (!TrainManagerBase.Renderer.ForceLegacyOpenGL && CarSections[SectionIndex].Groups[GroupIndex].Elements[ElementIndex].UpdateVAO)
786786
{
787-
VAOExtensions.CreateVAO(ref CarSections[SectionIndex].Groups[GroupIndex].Elements[ElementIndex].internalObject.Prototype.Mesh, true, TrainManagerBase.Renderer.DefaultShader.VertexLayout, TrainManagerBase.Renderer);
787+
VAOExtensions.CreateVAO(CarSections[SectionIndex].Groups[GroupIndex].Elements[ElementIndex].internalObject.Prototype.Mesh, true, TrainManagerBase.Renderer.DefaultShader.VertexLayout, TrainManagerBase.Renderer);
788788
}
789789
}
790790

@@ -832,7 +832,7 @@ private void UpdateCarSectionTouchElement(int SectionIndex, int GroupIndex, int
832832
CarSections[SectionIndex].Groups[GroupIndex].TouchElements[ElementIndex].Element.Update(baseTrain, Index, FrontAxle.Follower.TrackPosition - FrontAxle.Position, p, Direction, Up, Side, updatefunctions, Show, timeDelta, EnableDamping, true, CarSections[SectionIndex].Type == ObjectType.Overlay ? TrainManagerBase.Renderer.Camera : null);
833833
if (!TrainManagerBase.Renderer.ForceLegacyOpenGL && CarSections[SectionIndex].Groups[GroupIndex].TouchElements[ElementIndex].Element.UpdateVAO)
834834
{
835-
VAOExtensions.CreateVAO(ref CarSections[SectionIndex].Groups[GroupIndex].TouchElements[ElementIndex].Element.internalObject.Prototype.Mesh, true, TrainManagerBase.Renderer.DefaultShader.VertexLayout, TrainManagerBase.Renderer);
835+
VAOExtensions.CreateVAO(CarSections[SectionIndex].Groups[GroupIndex].TouchElements[ElementIndex].Element.internalObject.Prototype.Mesh, true, TrainManagerBase.Renderer.DefaultShader.VertexLayout, TrainManagerBase.Renderer);
836836
}
837837
}
838838

0 commit comments

Comments
 (0)