using System; using System.Threading; using System.Collections.Concurrent; namespace TrueCraft.Client.Rendering { /// /// Abstract base class for renderers of meshes. /// /// The object to render into a mesh. public abstract class Renderer : IDisposable { private readonly object _syncLock = new object(); /// /// /// public event EventHandler> MeshCompleted; private volatile bool _isRunning; private Thread _rendererThread; private ConcurrentQueue _items, _priorityItems; private volatile bool _isDisposed; /// /// Gets whether this renderer is running. /// public bool IsRunning { get { if (_isDisposed) throw new ObjectDisposedException(GetType().Name); return _isRunning; } } /// /// Gets whether this renderer is disposed of. /// public bool IsDisposed { get { return _isDisposed; } } /// /// /// protected Renderer() { lock (_syncLock) { _isRunning = false; _rendererThread = new Thread(DoRendering) { IsBackground = true }; _items = new ConcurrentQueue(); _priorityItems = new ConcurrentQueue(); _isDisposed = false; } } /// /// Starts this renderer. /// public void Start() { if (_isDisposed) throw new ObjectDisposedException(GetType().Name); if (_isRunning) return; lock (_syncLock) { _isRunning = true; _rendererThread.Start(null); } } /// /// /// /// private void DoRendering(object obj) { while (_isRunning) { var item = default(T); var result = default(Mesh); lock (_syncLock) { if (_priorityItems.TryDequeue(out item) && TryRender(item, out result)) { var args = new RendererEventArgs(item, result, true); if (MeshCompleted != null) MeshCompleted(this, args); } else if (_items.TryDequeue(out item) && TryRender(item, out result)) { var args = new RendererEventArgs(item, result, false); if (MeshCompleted != null) MeshCompleted(this, args); } } if (item == null) // We don't have any work, so sleep for a bit. Thread.Sleep(100); } } /// /// /// /// /// /// protected abstract bool TryRender(T item, out Mesh result); /// /// Stops this renderer. /// public void Stop() { if (_isDisposed) throw new ObjectDisposedException(GetType().Name); if (!_isRunning) return; lock (_syncLock) { _isRunning = false; _rendererThread.Join(); } } /// /// Enqueues an item to this renderer for rendering. /// /// /// public void Enqueue(T item, bool hasPriority = false) { if (_isDisposed) throw new ObjectDisposedException(GetType().Name); if (!_isRunning) return; lock (_syncLock) { if (hasPriority) _priorityItems.Enqueue(item); else _items.Enqueue(item); } } /// /// Disposes of this renderer. /// public void Dispose() { if (_isDisposed) return; Dispose(true); GC.SuppressFinalize(this); } /// /// Disposes of this renderer. /// /// protected virtual void Dispose(bool disposing) { Stop(); lock (_syncLock) { _rendererThread = null; _items = null; _priorityItems = null; _isDisposed = true; } } /// /// Finalizes this renderer. /// ~Renderer() { Dispose(false); } } }