mirror of
https://github.com/GTA-ASM/SanAndreasUnity
synced 2024-11-23 12:33:02 +00:00
32c0be1af2
* wip * much faster world creation * add StaticGeometryInspector * disable child/parent logic and fading * rename * (de)activate objects based on parent * set draw distance based on layers * ... * wip * wip * wip * remove unused param * prevent concurrent modification * ... * catch exceptions when notifying * ... * notify about area, not objects * limit public access to Area * ... * ... * allow public access * add public api * adapt code * pass callback to ctor * adapt focus points * fix * fix intersection check * support rectangles * adjust parameters in prefab * this should fix IsInsideOf() * ... * ... * fix getting area by index * create area if not exists * ... * ... * ... * wip for distance levels * remove constraint on generic parameter * add some validation * fix * fix draw distance per level * change time of day in which lights are visible * add todos * don't use id for UnRegisterFocusPoint() * use hash set for storing focus points * add 1 more level * mark area for update only if visibility changes * profile WorldSystem calls * add some profiling sections * limit time per frame for LoadingThread * switch custom concurrent queue * copy jobs to buffer * rename * change max draw distance setting * wait one more frame * try to remove 801 distance level to remove holes * attempt to hide interiors, but failed * delete no longer needed script * optimization * some error checking * add camera as focus point * dont add camera as focus point in headless mode * working on load priority * fix bug - load priority finished * ... * small optimizations * ... * ... * remove unneeded variable * add fading * dont do fading in headless mode * fadeRate available in inspector * change fade rate * take into account if geometry is loaded when checking if object should be visible, and if fading should be done * small optimization * cache IsInHeadlessMode * display Instance info in inspector * move interiors up in the sky * rename * adapt code to different y pos of interiors * refactor * fix finding matched enex for enexes that lead to the same interior level * display new world stats * rename * rename class * ... * ... * extract function * extract parameters into a struct * add focus point to dead body * add focus point to vehicle * add focus point to vehicle detached parts * remove OutOfRangeDestroyer from vehicle, and destroy vehicle if it falls below the map * dont use focus points on vehicle and vehicle detached parts, when not on server * add focus point for npc peds * add possibility to set timeout during which focus point keeps revealing after it's destroyed * adapt UnRegisterFocusPoint() to timeout * rename * adapt code * cleanup MapObject class * ... * converting to `lock()` * optimize method: use 1 lock instead of 3 * call OnObjectFinishedLoading() instead of AddToLoadedObjects() * ... * make sure it's main thread * AsyncLoader is no longer thread safe * convert static members to non-static in LoadingThread * fix * ... * store indexes for each area * impl GetAreaCenter() * calculate load priority based on distance to area, not objects ; limit time per frame ; sort area in Cell, not in concurrent SortedSet ; * add support for changing draw distance at runtime * delay setting the new value by 0.2 s * have a separate default max draw distance for mobile platforms * adjust y axis world params so that number of visible areas is reduced * remove "camera far clip plane" setting * rename * document flags * rename * disable shadow casting and receiving for some objects * allow casting shadows for LODs with large draw distance * remove "WorldSystem" layer * revert layer
214 lines
5 KiB
C#
214 lines
5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using UnityEngine;
|
|
using SanAndreasUnity.Utilities;
|
|
using System.Threading;
|
|
using System.Collections.Concurrent;
|
|
using Debug = UnityEngine.Debug;
|
|
|
|
namespace SanAndreasUnity.Behaviours
|
|
{
|
|
|
|
public class LoadingThread : MonoBehaviour {
|
|
|
|
// TODO: maybe convert to class, because it takes 36 bytes - it's too much for red-black tree operations
|
|
public struct Job<T>
|
|
{
|
|
public System.Func<T> action ;
|
|
public System.Action<T> callbackSuccess ;
|
|
public System.Action<System.Exception> callbackError ;
|
|
public System.Action<T> callbackFinish;
|
|
public float priority;
|
|
internal object result ;
|
|
internal System.Exception exception ;
|
|
internal long id;
|
|
}
|
|
|
|
public class JobComparer : IComparer<Job<object>>
|
|
{
|
|
public int Compare(Job<object> a, Job<object> b)
|
|
{
|
|
if (a.id == b.id)
|
|
return 0;
|
|
|
|
if (a.priority != b.priority)
|
|
return a.priority <= b.priority ? -1 : 1;
|
|
|
|
// priorities are the same
|
|
// the advantage has the job which was created earlier
|
|
|
|
return a.id <= b.id ? -1 : 1;
|
|
}
|
|
}
|
|
|
|
private class ThreadParameters
|
|
{
|
|
public readonly BlockingCollection<Job<object>> jobs =
|
|
new BlockingCollection<Job<object>> (new System.Collections.Concurrent.ConcurrentQueue<Job<object>>());
|
|
public readonly Utilities.ConcurrentQueue<Job<object>> processedJobs = new Utilities.ConcurrentQueue<Job<object>>();
|
|
private bool _shouldThreadExit = false;
|
|
private readonly object _shouldThreadExitLockObject = new object();
|
|
|
|
public bool ShouldThreadExit()
|
|
{
|
|
lock (_shouldThreadExitLockObject)
|
|
return _shouldThreadExit;
|
|
}
|
|
|
|
public void TellThreadToExit()
|
|
{
|
|
lock (_shouldThreadExitLockObject)
|
|
_shouldThreadExit = true;
|
|
}
|
|
}
|
|
|
|
public static LoadingThread Singleton { get; private set; }
|
|
|
|
private Thread _thread;
|
|
private readonly ThreadParameters _threadParameters = new ThreadParameters();
|
|
private readonly Queue<Job<object>> _processedJobsBuffer = new Queue<Job<object>>(256);
|
|
|
|
private static long s_lastJobId = 1;
|
|
private static readonly object s_lastJobIdLockObject = new object();
|
|
|
|
private readonly Stopwatch _stopwatch = new Stopwatch();
|
|
|
|
public ushort maxTimePerFrameMs = 0;
|
|
|
|
|
|
private void Awake()
|
|
{
|
|
Singleton = this;
|
|
}
|
|
|
|
void Start () {
|
|
|
|
_thread = new Thread (ThreadFunction);
|
|
_thread.Start(_threadParameters);
|
|
|
|
}
|
|
|
|
void OnDisable ()
|
|
{
|
|
if (_thread != null)
|
|
{
|
|
var sw = System.Diagnostics.Stopwatch.StartNew ();
|
|
// _thread.Interrupt ();
|
|
_threadParameters.TellThreadToExit ();
|
|
if (_thread.Join (7000))
|
|
Debug.LogFormat ("Stopped loading thread in {0} ms", sw.Elapsed.TotalMilliseconds);
|
|
else
|
|
Debug.LogError ("Failed to stop loading thread");
|
|
}
|
|
}
|
|
|
|
void Update () {
|
|
|
|
// get all processed jobs
|
|
|
|
_stopwatch.Restart();
|
|
|
|
Job<object> job;
|
|
|
|
while (true)
|
|
{
|
|
if (this.maxTimePerFrameMs != 0 && _stopwatch.ElapsedMilliseconds >= this.maxTimePerFrameMs)
|
|
break;
|
|
|
|
if (_processedJobsBuffer.Count > 0)
|
|
job = _processedJobsBuffer.Dequeue();
|
|
else
|
|
{
|
|
int numCopied = _threadParameters.processedJobs.DequeueToQueue(_processedJobsBuffer, 256);
|
|
if (numCopied == 0)
|
|
break;
|
|
|
|
job = _processedJobsBuffer.Dequeue();
|
|
}
|
|
|
|
if (job.exception != null)
|
|
{
|
|
// error happened
|
|
|
|
if (job.callbackError != null)
|
|
Utilities.F.RunExceptionSafe( () => job.callbackError (job.exception) );
|
|
|
|
Debug.LogException (job.exception);
|
|
}
|
|
else
|
|
{
|
|
// success
|
|
if (job.callbackSuccess != null)
|
|
Utilities.F.RunExceptionSafe( () => job.callbackSuccess (job.result) );
|
|
}
|
|
|
|
// invoke finish callback
|
|
if (job.callbackFinish != null)
|
|
F.RunExceptionSafe (() => job.callbackFinish (job.result));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
public static void RegisterJob<T> (Job<T> job)
|
|
{
|
|
// note: this function can be called from any thread
|
|
|
|
if (null == job.action)
|
|
throw new ArgumentException("Job must have an action");
|
|
|
|
if (0f == job.priority)
|
|
throw new ArgumentException("You forgot to assign job priority");
|
|
|
|
job.exception = null;
|
|
|
|
var j = new Job<object> () {
|
|
id = GetNextJobId(),
|
|
priority = job.priority,
|
|
action = () => job.action(),
|
|
callbackError = job.callbackError,
|
|
};
|
|
if(job.callbackSuccess != null)
|
|
j.callbackSuccess = (arg) => job.callbackSuccess( (T) arg );
|
|
if(job.callbackFinish != null)
|
|
j.callbackFinish = (arg) => job.callbackFinish( (T) arg );
|
|
|
|
Singleton._threadParameters.jobs.Add (j);
|
|
}
|
|
|
|
static long GetNextJobId()
|
|
{
|
|
lock (s_lastJobIdLockObject)
|
|
{
|
|
return s_lastJobId++;
|
|
}
|
|
}
|
|
|
|
static void ThreadFunction (object objectParameter)
|
|
{
|
|
ThreadParameters threadParameters = (ThreadParameters) objectParameter;
|
|
|
|
while (!threadParameters.ShouldThreadExit())
|
|
{
|
|
Job<object> job;
|
|
if (!threadParameters.jobs.TryTake (out job, 200))
|
|
continue;
|
|
|
|
try
|
|
{
|
|
job.result = job.action();
|
|
}
|
|
catch(System.Exception ex)
|
|
{
|
|
job.exception = ex;
|
|
}
|
|
|
|
threadParameters.processedJobs.Enqueue(job);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|