port from perforce

This commit is contained in:
2026-04-18 22:31:51 +02:00
commit 8d0ab5b7cc
8409 changed files with 3972376 additions and 0 deletions

View 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
}
}

View File

@@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Aiwaz.Contracts;
using System.Runtime.InteropServices;
using Aiwaz.Core;
namespace Aiwaz.Resources
{
public enum CommandFlags : byte
{
None = 0x00,
SubChainStart = 0x01, // Command will start a new command chain (UseShader for example)
SubChainEnd = 0x02, // Command will end a command chain (Render)
Unique = 0x04, // Similar commands should not be reduced to one Command
StartChain = 0x08, // Command will generate a new command chain
EndChain = 0x10, // Command will end the command chain (RenderGeometry for example)
FlushChain = 0x20, // Command will end the command chain (RenderGeometry for example)
}
public class Command
{
[StructLayout(LayoutKind.Explicit)]
public struct CommandInfoUnion
{
[FieldOffset(0)]
public uint RawValue;
[FieldOffset(0)]
public byte Priority;
[FieldOffset(1)]
public byte SubPriority;
[FieldOffset(2)]
public CommandFlags Flags;
[FieldOffset(3)]
public byte Type;
}
public Command(CommandUser argOwner, CommandFlags argFlags, byte argType)
: this(argOwner, argFlags, argType, 0, 0)
{
}
public Command(CommandUser argOwner, CommandFlags argFlags, byte argType, byte argPriority)
: this(argOwner, argFlags, argType, argPriority, 0)
{
}
public Command(CommandUser argOwner, CommandFlags argFlags, byte argType, byte argPriority, byte argSubPriority)
{
CommandInfo.RawValue = 0;
Owner = argOwner;
Flags = argFlags;
Priority = argPriority;
SubPriority = argSubPriority;
Type = argType;
}
public CommandInfoUnion CommandInfo;
public byte Priority { get { return CommandInfo.Priority; } set { CommandInfo.Priority = value; } }
public byte SubPriority { get { return CommandInfo.SubPriority; } set { CommandInfo.SubPriority = value; } }
public CommandFlags Flags { get { return CommandInfo.Flags; } set { CommandInfo.Flags = value; } }
public byte Type { get { return CommandInfo.Type; } set { CommandInfo.Type = value; } }
public CommandUser Owner;
};
public enum CommandExecuteResult
{
None, // Execution done, proceed to the next Command
RetrySubChain, // Execution should start from a previous "SubChainStart"-flagged Command
RetrySubChainSkipHead // Equal to the above result but skips the as "SubChainStart"-flagged Command
};
public abstract class CommandUser : Resource, IDisposable
{
private List<RenderCommandNode> assignedRenderCommandNodes = new List<RenderCommandNode>();
public List<Command> Commands { get; private set; }
public bool IsPreconditionForNextCommands { get; set; }
public CommandUser()
{
this.Commands = new List<Command>();
Engine.RegisterEngineDisposable(this);
}
~CommandUser()
{
if (!Engine.Initialized)
return;
if (Engine.UnregisterEngineDisposable(this))
this.Dispose();
}
public abstract CommandExecuteResult ExecuteCommand(byte argCommandType, CommandBuffer argCurrentBuffer, int argCurrentPositon);
public void AssignToRenderCommandNode(RenderCommandNode node)
{
assignedRenderCommandNodes.Add(node);
node.MarkDirty();
}
public void UnassignFromRenderCommandNode(RenderCommandNode node)
{
if (assignedRenderCommandNodes.Remove(node))
node.MarkDirty();
}
protected void UnassignFromSceneNodes()
{
for (var i = 0; i < assignedRenderCommandNodes.Count; ++i )
assignedRenderCommandNodes[i].Children.Remove(this);
assignedRenderCommandNodes.Clear();
}
protected void MarkCommandsAsDirty()
{
foreach (var node in assignedRenderCommandNodes)
node.MarkDirty();
}
public new virtual void Dispose()
{
this.WellKnownName = null;
this.UnassignFromSceneNodes();
Engine.UnregisterEngineDisposable(this);
base.Dispose();
}
}
}