Performance Improvement of IMMaterialPool.GetMaterial()
Hi.
I have always used it.
I am running a large number of Draw.Quad() in Immediate Mode in Shapes and am having trouble with the frame rate drop that occurs around 1300 executions.
I have investigated in Unity's Deep Profile and found that it is mainly the execution of the following methods that are taking a long time.
- IMMaterialPool.GetMaterial()
- IMDrawer.GetMaterialKeywords()
- MetaMpb.PreAppendCheck()
Of these, we noted that TryGetValue() is the reason why IMMaterialPool.GetMaterial() is taking so long to process.
I made a small improvement myself.
Before:
IMDrawer.cs
if( DrawCommand.IsAddingDrawCommandsToBuffer ) { Draw.style.renderState.shader = sourceMat.shader; Draw.style.renderState.keywords = GetMaterialKeywords( sourceMat ); Draw.style.renderState.isTextMaterial = drawType == DrawType.TextPooledPersistent || drawType == DrawType.TextAssetClone;
RenderState.cs
public Shader shader; public string[] keywords; // this is gross
public bool Equals( RenderState other ) => Equals( shader, other.shader ) && StrArrEquals( keywords, other.keywords ) && zTest == other.zTest &&
After:
IMDrawer.cs
if( DrawCommand.IsAddingDrawCommandsToBuffer ) { Draw.style.renderState.shader = sourceMat.shader; Draw.style.renderState.isTextMaterial = drawType == DrawType.TextPooledPersistent || drawType == DrawType.TextAssetClone; string[] keywords = GetMaterialKeywords(sourceMat); Draw.style.renderState.keywords = keywords; Draw.style.renderState.comparisonKeywords = string.Join(string.Empty, keywords);
*I believe that "string.Empty" should be a string that is never used as a keyword.
RenderState.cs
public Shader shader; public string[] keywords; // this is gross public string comparisonKeywords;
public bool Equals( RenderState other ) => Equals( shader, other.shader ) && comparisonKeywords == other.comparisonKeywords && zTest == other.zTest &&
GetHashCode() in RenderState.cs has also been improved.
public override int GetHashCode() { unchecked { int hashCode = ( shader != null ? shader.GetHashCode() : 0 ); if (comparisonKeywords != null) hashCode = ( hashCode * 397 ) ^ comparisonKeywords.GetHashCode(); hashCode = ( hashCode * 397 ) ^ (int)zTest; hashCode = ( hashCode * 397 ) ^ zOffsetFactor.GetHashCode(); hashCode = ( hashCode * 397 ) ^ zOffsetUnits; hashCode = ( hashCode * 397 ) ^ (int)colorMask; hashCode = ( hashCode * 397 ) ^ (int)stencilComp; hashCode = ( hashCode * 397 ) ^ (int)stencilOpPass; hashCode = ( hashCode * 397 ) ^ (stencilRefID << 16) | (stencilReadMask << 8) | stencilWriteMask; return hashCode; } }
The results of the before and after profiles are also attached.
*4727 is the number of calls.
Before:
After:
It may be that cases like mine that are executed in large numbers are rare, or it may be that there is nothing we can do about it due to the processing, but is there any other room for improvement?
EDIT: The long execution time is due to the use of Deep Profile. If it is not used, the time can be as low as 6-8 ms, but the problem becomes more apparent when executed on mobile devices.
I know I should not have originally altered the code provided, but I have done further research since the above.
As a result, we have further improved the performance.
I have sent you an email with the improved code, though it may be inconvenient for you.
(Since the content is the Shapes source code itself, we decided it was not appropriate to post it in this forum.)
After applying the code, all drawing methods, including Draw.Quad(), are about twice as fast.
Before:
After:
There are three main improvements.
However, this is still not the performance I am looking for.
Apart from the above, I was able to improve it a bit by not running ColorSpaceAdjusted() in Quad_Internal().
(This is because my project's color space is Gamma)
Upon closer inspection, it seems that Draw.Quad() alone was running about 10000 times in my project.
It is no wonder this is slowing me down, so I am considering reducing the number of calls to Draw.Quad() in the first place.
Shapes is a great asset and I hope to continue using it.
If there is anything else we can do, please let us know.