New command queue implementation with hopefully less GC invocations.
This commit is contained in:
parent
39a9f67367
commit
9eadc26f2f
163
Quik/CommandQueue/CommandEngine.cs
Normal file
163
Quik/CommandQueue/CommandEngine.cs
Normal file
@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Quik.CommandQueue
|
||||
{
|
||||
public class CommandEngine
|
||||
{
|
||||
private int _zIndex = 0;
|
||||
private readonly Stack<int> _zStack = new Stack<int>();
|
||||
public int ZIndex => _zIndex;
|
||||
|
||||
private QuikRectangle _viewport;
|
||||
private readonly Stack<QuikRectangle> _viewportStack = new Stack<QuikRectangle>();
|
||||
private readonly Stack<object> _matrixStack = new Stack<object>();
|
||||
|
||||
private Command _customCommandBase = Command.CustomCommandBase;
|
||||
private readonly List<QuikCommandHandler> _customCommands = new List<QuikCommandHandler>();
|
||||
|
||||
public QuikRectangle Viewport { get; }
|
||||
|
||||
// TODO: Make a real matrix class.
|
||||
public float[] ActiveTransforms { get; }
|
||||
|
||||
public object ActiveStyle { get; }
|
||||
|
||||
protected CommandEngine()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public Command RegisterCustomCommand(QuikCommandHandler handler)
|
||||
{
|
||||
Command id = _customCommandBase++;
|
||||
_customCommands.Insert(id - Command.CustomCommandBase, handler);
|
||||
return id;
|
||||
}
|
||||
|
||||
public void ProcessCommands(QuikRectangle bounds, CommandQueue queue)
|
||||
{
|
||||
if (!queue.Peek().IsCommand)
|
||||
throw new ArgumentException("The first element in the queue must be a command frame.");
|
||||
|
||||
Reset();
|
||||
|
||||
_viewport = bounds;
|
||||
_viewportStack.Push(_viewport);
|
||||
|
||||
Frame frame;
|
||||
while (queue.TryDequeue(out frame))
|
||||
{
|
||||
Command cmd = (Command)frame;
|
||||
switch (cmd)
|
||||
{
|
||||
default:
|
||||
if (cmd > Command.CustomCommandBase)
|
||||
{
|
||||
_customCommands[cmd - Command.CustomCommandBase].Invoke(this, queue);
|
||||
}
|
||||
else
|
||||
{
|
||||
ChildProcessCommand(cmd, queue);
|
||||
}
|
||||
break;
|
||||
|
||||
case Command.ConditionalBegin: ConditionalHandler(queue); break;
|
||||
case Command.ConditionalEnd: /* nop */ break;
|
||||
|
||||
case Command.Invoke:
|
||||
queue.Dequeue().As<QuikCommandHandler>().Invoke(this, queue);
|
||||
break;
|
||||
case Command.PushViewport:
|
||||
_viewportStack.Push(_viewport);
|
||||
break;
|
||||
case Command.IntersectViewport:
|
||||
_viewport = QuikRectangle.Intersect((QuikRectangle)queue.Dequeue(), _viewport);
|
||||
break;
|
||||
case Command.StoreViewport:
|
||||
_viewport = (QuikRectangle)queue.Dequeue();
|
||||
break;
|
||||
case Command.PopViewport:
|
||||
_viewport = _viewportStack.TryPop(out QuikRectangle viewport) ? viewport : bounds;
|
||||
break;
|
||||
case Command.PushZ:
|
||||
_zStack.Push(_zIndex);
|
||||
break;
|
||||
case Command.IncrementZ:
|
||||
_zIndex++;
|
||||
break;
|
||||
case Command.AddZ:
|
||||
_zIndex += (int)queue.Dequeue();
|
||||
break;
|
||||
case Command.StoreZ:
|
||||
_zIndex = (int)queue.Dequeue();
|
||||
break;
|
||||
case Command.DecrementZ:
|
||||
_zIndex--;
|
||||
break;
|
||||
case Command.PopZ:
|
||||
_zIndex = _zStack.TryPop(out int zindex) ? zindex : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ChildProcessCommand(Command name, CommandQueue queue)
|
||||
{
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
_zIndex = 0;
|
||||
_zStack.Clear();
|
||||
|
||||
_viewport = new QuikRectangle(float.MaxValue, float.MaxValue, float.MinValue, float.MinValue);
|
||||
_viewportStack.Clear();
|
||||
|
||||
_matrixStack.Clear();
|
||||
_matrixStack.Push(null); // TODO: replace with identity matrix.
|
||||
}
|
||||
|
||||
private void ConditionalHandler(CommandQueue queue)
|
||||
{
|
||||
Frame frame = queue.Dequeue();
|
||||
|
||||
if (
|
||||
frame.IsInteger && (int)frame != 0 ||
|
||||
frame.As<Func<bool>>().Invoke())
|
||||
{
|
||||
// Take this branch.
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip this branch.
|
||||
int depth = 1;
|
||||
while (queue.TryPeek(out frame))
|
||||
{
|
||||
if (!frame.IsCommand)
|
||||
{
|
||||
queue.Dequeue();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ((Command)frame)
|
||||
{
|
||||
case Command.ConditionalBegin:
|
||||
// Increment conditional depth.
|
||||
depth++;
|
||||
break;
|
||||
case Command.ConditionalEnd:
|
||||
// Decrement condional depth, exit if zero.
|
||||
if (--depth == 0)
|
||||
{
|
||||
queue.Dequeue();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
queue.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,5 +4,5 @@ namespace Quik.CommandQueue
|
||||
/// A delegate for a QUIK command.
|
||||
/// </summary>
|
||||
/// <param name="stack">The current stack.</param>
|
||||
public delegate void QuikCommandHandler(object state);
|
||||
public delegate void QuikCommandHandler(CommandEngine state, CommandQueue queue);
|
||||
}
|
Loading…
Reference in New Issue
Block a user