port from perforce
This commit is contained in:
342
aiwaz/Backup/Aiwaz.Resources/Commands/CommandBuffer.cs
Normal file
342
aiwaz/Backup/Aiwaz.Resources/Commands/CommandBuffer.cs
Normal file
@@ -0,0 +1,342 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Aiwaz.Contracts;
|
||||
using Aiwaz.Core;
|
||||
|
||||
namespace Aiwaz.Resources
|
||||
{
|
||||
public class CommandBuffer
|
||||
{
|
||||
private class CommandChainInternal
|
||||
{
|
||||
public int Priority = 0;
|
||||
public List<Command> Chain;
|
||||
|
||||
public void UpdatePriority()
|
||||
{
|
||||
Priority = -1;
|
||||
foreach (var command in Chain)
|
||||
if ((command.Flags & CommandFlags.SubChainStart) != CommandFlags.None)
|
||||
{
|
||||
Priority = command.SubPriority;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandComparsion : IComparer<Command>
|
||||
{
|
||||
public int Compare(Command x, Command y)
|
||||
{
|
||||
return x.Priority.CompareTo(y.Priority);
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandChainComparsion : IComparer<CommandChainInternal>
|
||||
{
|
||||
public int Compare(CommandChainInternal x, CommandChainInternal y)
|
||||
{
|
||||
if (x.Priority == y.Priority)
|
||||
return x.Chain.Count.CompareTo(y.Chain.Count);
|
||||
return x.Priority.CompareTo(y.Priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CommandBuffer optimizedCommandBuffer = null;
|
||||
private List<CommandChainInternal> gatheredCommandChains = new List<CommandChainInternal>();
|
||||
|
||||
public List<Command> BufferedCommands { get; protected set; }
|
||||
public List<CommandBuffer> ChildCommandBuffers { get; protected set; }
|
||||
public CommandBuffer ParentBuffer { get; set; }
|
||||
|
||||
#region ICommandBuffer Members
|
||||
|
||||
public CommandBuffer()
|
||||
{
|
||||
BufferedCommands = new List<Command>();
|
||||
ChildCommandBuffers = new List<CommandBuffer>();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
BufferedCommands.Clear();
|
||||
ChildCommandBuffers.Clear();
|
||||
gatheredCommandChains.Clear();
|
||||
if (optimizedCommandBuffer != null)
|
||||
optimizedCommandBuffer.Clear();
|
||||
}
|
||||
|
||||
public void Perform(bool useOptimizedList)
|
||||
{
|
||||
if (!useOptimizedList)
|
||||
{
|
||||
int subChainStartCommandIndex = -1;
|
||||
for (int i = 0; i < BufferedCommands.Count;)
|
||||
{
|
||||
var command = BufferedCommands[i];
|
||||
|
||||
if ((command.Flags & CommandFlags.SubChainStart) != CommandFlags.None)
|
||||
subChainStartCommandIndex = i;
|
||||
|
||||
switch (command.Owner.ExecuteCommand(command.Type, this, i))
|
||||
{
|
||||
case CommandExecuteResult.None:
|
||||
if ((command.Flags & CommandFlags.SubChainEnd) != CommandFlags.None)
|
||||
subChainStartCommandIndex = -1;
|
||||
i++;
|
||||
break;
|
||||
case CommandExecuteResult.RetrySubChain:
|
||||
if (subChainStartCommandIndex == -1)
|
||||
throw new ActionFailedException("Failed to restart sub command chain.");
|
||||
else
|
||||
i = subChainStartCommandIndex;
|
||||
break;
|
||||
case CommandExecuteResult.RetrySubChainSkipHead:
|
||||
if (subChainStartCommandIndex == -1)
|
||||
throw new ActionFailedException("Failed to restart headless sub command chain.");
|
||||
else
|
||||
i = subChainStartCommandIndex + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (optimizedCommandBuffer != null)
|
||||
optimizedCommandBuffer.Perform(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Optimize()
|
||||
{
|
||||
if (optimizedCommandBuffer == null)
|
||||
optimizedCommandBuffer = new CommandBuffer();
|
||||
optimizedCommandBuffer.Clear();
|
||||
|
||||
List<Command> tempChain = new List<Command>();
|
||||
this.SearchDrawableCommandChain(this, ref tempChain);
|
||||
this.FlushGatheredCommandChains();
|
||||
}
|
||||
|
||||
public void AddChildCommandBuffer(CommandBuffer commandBuffer)
|
||||
{
|
||||
ChildCommandBuffers.Add(commandBuffer);
|
||||
commandBuffer.ParentBuffer = this;
|
||||
}
|
||||
|
||||
public void RemoveChildCommandBuffer(CommandBuffer commandBuffer)
|
||||
{
|
||||
if (ChildCommandBuffers.Remove(commandBuffer))
|
||||
commandBuffer.ParentBuffer = null;
|
||||
}
|
||||
|
||||
private void SearchDrawableCommandChain(CommandBuffer incomingBuffer, ref List<Command> currentChain)
|
||||
{
|
||||
for (int i = 0; i < incomingBuffer.BufferedCommands.Count;)
|
||||
{
|
||||
var currentCommand = incomingBuffer.BufferedCommands[i];
|
||||
var commandFlags = currentCommand.Flags;
|
||||
|
||||
// A command is start AND end of a command chain, this indicates that a serious state change will come,
|
||||
// so we will need to flush the current command chains.
|
||||
if ((commandFlags & CommandFlags.FlushChain) != 0)
|
||||
{
|
||||
this.FlushGatheredCommandChains();
|
||||
currentChain.Clear();
|
||||
}
|
||||
// Old way commented out due problems with transformations before shaders
|
||||
// A command was found which does not belong to a build up current chain (due no chain is building herself up..)
|
||||
// so we drop it, we simply do not care about some run away
|
||||
/*if (currentChain.empty()
|
||||
&& (commandFlags & CommandFlags.StartChain) == 0
|
||||
&& (commandFlags & CommandFlags.EndChain) == 0)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}*/
|
||||
// New way is to search the list for a start chain command, then we put this command after the start chain command
|
||||
if (currentChain.Count == 0
|
||||
&& (commandFlags & CommandFlags.StartChain) == 0
|
||||
&& (commandFlags & CommandFlags.EndChain) == 0)
|
||||
{
|
||||
var foundPlace = false;
|
||||
var commands = incomingBuffer.BufferedCommands;
|
||||
for (int k = i + 1; k < commands.Count; ++k)
|
||||
if ((commands[k].Flags & CommandFlags.StartChain) != CommandFlags.None)
|
||||
{
|
||||
commands.Insert(k + 1, currentCommand);
|
||||
commands.RemoveAt(i);
|
||||
foundPlace = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundPlace)
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
// A new command chain should be initialized, clear our current temp chain and add this command into it.
|
||||
// All old commands remaining in the chain are already used or where completely useless (SetShader . SetParameter . NextPass).
|
||||
else if ((commandFlags & CommandFlags.StartChain) != 0 && currentChain.Count > 0)
|
||||
{
|
||||
currentChain.Clear();
|
||||
currentChain.Add(currentCommand);
|
||||
++i;
|
||||
}
|
||||
// We should end our chain, now we need to optimize this chain and add this into the gathered chain list,
|
||||
// at the next flush command we will put this list into our optimized list, with some more optimizations.
|
||||
else if ((commandFlags & CommandFlags.EndChain) != 0)
|
||||
{
|
||||
// We have a filled chain (at least 1 operation following by this operation)
|
||||
if (currentChain.Count > 0)
|
||||
{
|
||||
var commandChainInternal = new CommandChainInternal();
|
||||
commandChainInternal.Chain = new List<Command>(currentChain);
|
||||
|
||||
commandChainInternal.Chain.Add(currentCommand);
|
||||
|
||||
commandChainInternal.Chain.Sort(new CommandChainInternal.CommandComparsion());
|
||||
|
||||
//m_OptimizedCommandBuffer.get_BufferedCommands().insert(m_OptimizedCommandBuffer.get_BufferedCommands().end(), chain.begin(), chain.end());
|
||||
gatheredCommandChains.Add(commandChainInternal);
|
||||
|
||||
// Search for the SubChainEnd flagged command , this should be found right after the SubChainStart flagged commands
|
||||
// both commands will mark the beginning of our chain . delete everything behind this command will ensure
|
||||
// that we will use a fresh new command chain, only filled with what we need (StartSubChain . EndSubChain)
|
||||
for (int k = 0; k < currentChain.Count; ++k)
|
||||
if ((currentChain[k].Flags & CommandFlags.SubChainEnd) != CommandFlags.None)
|
||||
{
|
||||
currentChain.RemoveRange(k + 1, currentChain.Count - (k + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Our chain was empty, this means a solo command was found, put it into our optimized list and continue,
|
||||
// there is not much to do here.
|
||||
else
|
||||
{
|
||||
optimizedCommandBuffer.BufferedCommands.Add(currentCommand);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
// No start and no end of the command chain, just put this command into our chain,
|
||||
// commands of this type are for example, SetVertexBuffer, SetIndexBuffer, SetTransformation,...
|
||||
else
|
||||
{
|
||||
currentChain.Add(currentCommand);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// We may have child buffers, continue our search there.
|
||||
foreach (var buffer in incomingBuffer.ChildCommandBuffers)
|
||||
{
|
||||
var newCurrentChain = new List<Command>(currentChain);
|
||||
this.SearchDrawableCommandChain(buffer, ref currentChain);
|
||||
currentChain = newCurrentChain;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FlushGatheredCommandChains()
|
||||
{
|
||||
var flushedCommands = new List<Command>();
|
||||
gatheredCommandChains.ForEach(item => item.UpdatePriority());
|
||||
while (gatheredCommandChains.Count > 0)
|
||||
{
|
||||
gatheredCommandChains.Sort(new CommandChainInternal.CommandChainComparsion());
|
||||
|
||||
int currentPriority = 0;
|
||||
List<int> checkables = new List<int>();
|
||||
for (int i = 0; i < gatheredCommandChains.Count;)
|
||||
{
|
||||
if (gatheredCommandChains[i].Chain.Count == 0)
|
||||
gatheredCommandChains.RemoveAt(i);
|
||||
else if (checkables.Count == 0 || currentPriority == gatheredCommandChains[i].Priority)
|
||||
{
|
||||
currentPriority = gatheredCommandChains[i].Priority;
|
||||
checkables.Add(i);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (checkables.Count == 1)
|
||||
{
|
||||
int thisIndex = checkables.First();
|
||||
flushedCommands.AddRange(gatheredCommandChains[thisIndex].Chain);
|
||||
gatheredCommandChains.RemoveAt(thisIndex);
|
||||
if (gatheredCommandChains.Count == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
List<Command> optimizedCommandChain = new List<Command>();
|
||||
|
||||
Command lastCommand = null;
|
||||
while (checkables.Count > 1)
|
||||
{
|
||||
if (checkables.Count > 0 && checkables.First() != 0)
|
||||
break;
|
||||
for (int i = 0; i < checkables.Count;)
|
||||
{
|
||||
int thisIndex = checkables[i];
|
||||
var currentCommandChainInternal = gatheredCommandChains[thisIndex];
|
||||
var thisCommand = currentCommandChainInternal.Chain.First();
|
||||
if (i == 0 || (thisCommand.CommandInfo.RawValue == lastCommand.CommandInfo.RawValue && thisCommand.Owner == lastCommand.Owner))
|
||||
{
|
||||
// we found a similarity (or the first element), put this into our optimized temp list
|
||||
lastCommand = thisCommand;
|
||||
if (i == 0 || (thisCommand.Flags & CommandFlags.Unique) != 0)
|
||||
optimizedCommandChain.Add(thisCommand);
|
||||
currentCommandChainInternal.Chain.RemoveAt(0);
|
||||
if (currentCommandChainInternal.Chain.Count == 0)
|
||||
{
|
||||
checkables.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
else
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do not check this list further, a difference was detected
|
||||
checkables.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
// resort our gatheredCommandChains so that we could use the remaining content of a list properly, only do that if the checkable list has no direct preceeding elements ({0,2,3} instead {0,1,2})
|
||||
if (checkables.Count > 0)
|
||||
for (int i = checkables.First() + 1; i < checkables.Count; ++i)
|
||||
if (checkables[i] != i)
|
||||
{
|
||||
var tmp = gatheredCommandChains[i];
|
||||
gatheredCommandChains[i] = gatheredCommandChains[checkables[i]];
|
||||
gatheredCommandChains[checkables[i]] = tmp;
|
||||
checkables[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
flushedCommands.AddRange(optimizedCommandChain);
|
||||
}
|
||||
|
||||
// remove unnecessary SubChainEnd commands
|
||||
bool openPass = false;
|
||||
for (int i = flushedCommands.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if ((flushedCommands[i].Flags & CommandFlags.SubChainEnd) == CommandFlags.SubChainEnd)
|
||||
{
|
||||
if (!openPass)
|
||||
openPass = true;
|
||||
else
|
||||
flushedCommands.RemoveAt(i);
|
||||
}
|
||||
else if ((flushedCommands[i].Flags & CommandFlags.SubChainStart) == CommandFlags.SubChainStart)
|
||||
openPass = false;
|
||||
|
||||
}
|
||||
optimizedCommandBuffer.BufferedCommands.AddRange(flushedCommands);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user