mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-13 17:47:12 -04:00
Optimise string allocations, some other reductions in allocations.
This commit is contained in:
parent
bb2a55944f
commit
0352a4da4c
@ -96,8 +96,8 @@ namespace ClassicalSharp {
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
if( !String.IsNullOrEmpty( textInput.chatInputText ) ) {
|
||||
Window.chatInInputBuffer = textInput.chatInputText;
|
||||
if( !textInput.chatInputText.Empty ) {
|
||||
Window.chatInInputBuffer = textInput.chatInputText.ToString();
|
||||
}
|
||||
chatFont.Dispose();
|
||||
chatBoldFont.Dispose();
|
||||
@ -143,7 +143,7 @@ namespace ClassicalSharp {
|
||||
announcementDisplayTime = DateTime.UtcNow;
|
||||
if( !String.IsNullOrEmpty( text ) ) {
|
||||
List<DrawTextArgs> parts = Utils2D.SplitText( GraphicsApi, text, true );
|
||||
Size size = Utils2D.MeasureSize( Utils.StripColours( text ), announcementFont, true );
|
||||
Size size = Utils2D.MeasureSize( parts, announcementFont, true );
|
||||
int x = Window.Width / 2 - size.Width / 2;
|
||||
int y = Window.Height / 4 - size.Height / 2;
|
||||
announcementTexture = Utils2D.MakeTextTexture( parts, announcementFont, size, x, y );
|
||||
@ -180,7 +180,8 @@ namespace ClassicalSharp {
|
||||
void OpenTextInputBar( string initialText ) {
|
||||
suppressNextPress = true;
|
||||
HandlesAllInput = true;
|
||||
textInput.chatInputText = initialText;
|
||||
textInput.chatInputText.Clear();
|
||||
textInput.chatInputText.Append( 0, initialText );
|
||||
textInput.Init();
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ namespace ClassicalSharp {
|
||||
|
||||
Texture ModifyText( int yOffset, string text, Font font ) {
|
||||
List<DrawTextArgs> parts = Utils2D.SplitText( GraphicsApi, text, true );
|
||||
Size size = Utils2D.MeasureSize( Utils.StripColours( text ), font, true );
|
||||
Size size = Utils2D.MeasureSize( parts, font, true );
|
||||
int x = Window.Width / 2 - size.Width / 2;
|
||||
int y = Window.Height / 2 - yOffset;
|
||||
return Utils2D.MakeTextTexture( parts, font, size, x, y );
|
||||
|
@ -6,8 +6,10 @@ namespace ClassicalSharp {
|
||||
public class FpsScreen : Screen {
|
||||
|
||||
readonly Font font;
|
||||
UnsafeString text;
|
||||
public FpsScreen( Game window ) : base( window ) {
|
||||
font = new Font( "Arial", 13 );
|
||||
text = new UnsafeString( 96 );
|
||||
}
|
||||
|
||||
TextWidget fpsTextWidget;
|
||||
@ -17,20 +19,24 @@ namespace ClassicalSharp {
|
||||
fpsTextWidget.Render( delta );
|
||||
}
|
||||
|
||||
const string formatString = "FPS: {0} (min {1}), chunks/s: {2}, vertices: {3}";
|
||||
double accumulator, maxDelta;
|
||||
int fpsCount;
|
||||
|
||||
void UpdateFPS( double delta ) {
|
||||
int fpsCount;
|
||||
unsafe void UpdateFPS( double delta ) {
|
||||
fpsCount++;
|
||||
maxDelta = Math.Max( maxDelta, delta );
|
||||
accumulator += delta;
|
||||
long vertices = Window.Vertices;
|
||||
|
||||
if( accumulator >= 1 ) {
|
||||
string text = String.Format( formatString, (int)( fpsCount / accumulator ),
|
||||
(int)( 1f / maxDelta ), Window.ChunkUpdates, vertices );
|
||||
fpsTextWidget.SetText( text );
|
||||
fixed( char* ptr = text.value ) {
|
||||
char* ptr2 = ptr;
|
||||
text.Clear( ptr2 )
|
||||
.Append( ref ptr2, "FPS: " ).AppendNum( ref ptr2, (int)( fpsCount / accumulator ) )
|
||||
.Append( ref ptr2, " (min " ).AppendNum( ref ptr2, (int)( 1f / maxDelta ) )
|
||||
.Append( ref ptr2, "), chunks/s: " ).AppendNum( ref ptr2, Window.ChunkUpdates )
|
||||
.Append( ref ptr2, ", vertices: " ).AppendNum( ref ptr2, Window.Vertices );
|
||||
}
|
||||
fpsTextWidget.SetText( text.value );
|
||||
maxDelta = 0;
|
||||
accumulator = 0;
|
||||
fpsCount = 0;
|
||||
|
@ -38,7 +38,7 @@ namespace ClassicalSharp {
|
||||
GroupName = p.GroupName;
|
||||
GroupRank = p.GroupRank;
|
||||
List<DrawTextArgs> parts = Utils2D.SplitText( graphics, Name, true );
|
||||
Size size = Utils2D.MeasureSize( Utils.StripColours( Name ), font, true );
|
||||
Size size = Utils2D.MeasureSize( parts, font, true );
|
||||
Texture = Utils2D.MakeTextTexture( parts, font, size, 0, 0 );
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace ClassicalSharp {
|
||||
Name = p.DisplayName;
|
||||
PlayerId = p.ID;
|
||||
List<DrawTextArgs> parts = Utils2D.SplitText( graphics, Name, true );
|
||||
Size size = Utils2D.MeasureSize( Utils.StripColours( Name ), font, true );
|
||||
Size size = Utils2D.MeasureSize( parts, font, true );
|
||||
Texture = Utils2D.MakeTextTexture( parts, font, size, 0, 0 );
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ namespace ClassicalSharp {
|
||||
Size size = new Size( 0, defaultHeight );
|
||||
if( !String.IsNullOrEmpty( text ) ) {
|
||||
parts = Utils2D.SplitText( GraphicsApi, text, true );
|
||||
size = Utils2D.MeasureSize( Utils.StripColours( text ), font, true );
|
||||
size = Utils2D.MeasureSize( parts, font, true );
|
||||
}
|
||||
|
||||
int x = HorizontalDocking == Docking.LeftOrTop ? XOffset : Window.Width - size.Width - XOffset;
|
||||
|
@ -6,7 +6,7 @@ using System.Windows.Forms;
|
||||
namespace ClassicalSharp {
|
||||
|
||||
public sealed class TextInputWidget : Widget {
|
||||
|
||||
|
||||
public TextInputWidget( Game window, Font font, Font boldFont ) : base( window ) {
|
||||
HorizontalDocking = Docking.LeftOrTop;
|
||||
VerticalDocking = Docking.BottomOrRight;
|
||||
@ -19,6 +19,7 @@ namespace ClassicalSharp {
|
||||
typingLogPos = Window.ChatInputLog.Count; // Index of newest entry + 1.
|
||||
this.font = font;
|
||||
this.boldFont = boldFont;
|
||||
chatInputText = new UnsafeString( 64 );
|
||||
}
|
||||
|
||||
Texture chatInputTexture, chatCaretTexture;
|
||||
@ -26,7 +27,7 @@ namespace ClassicalSharp {
|
||||
int caretPos = -1;
|
||||
int typingLogPos = 0;
|
||||
public int ChatInputYOffset;
|
||||
public string chatInputText = "";
|
||||
internal UnsafeString chatInputText;
|
||||
readonly Font font, boldFont;
|
||||
|
||||
public override void Render( double delta ) {
|
||||
@ -39,19 +40,20 @@ namespace ClassicalSharp {
|
||||
X = 10;
|
||||
DrawTextArgs caretArgs = new DrawTextArgs( GraphicsApi, "_", Color.White, false );
|
||||
chatCaretTexture = Utils2D.MakeTextTexture( boldFont, 0, 0, ref caretArgs );
|
||||
string value = chatInputText.value;
|
||||
|
||||
if( chatInputText.Length == 0 ) {
|
||||
if( chatInputText.Empty ) {
|
||||
caretPos = -1;
|
||||
}
|
||||
Size size = Utils2D.MeasureSize( chatInputText, font, false );
|
||||
Size size = Utils2D.MeasureSize( value, font, false );
|
||||
|
||||
if( caretPos == -1 ) {
|
||||
chatCaretTexture.X1 = 10 + size.Width;
|
||||
size.Width += chatCaretTexture.Width;
|
||||
} else {
|
||||
Size trimmedSize = Utils2D.MeasureSize( chatInputText.Substring( 0, caretPos ), font, false );
|
||||
Size trimmedSize = Utils2D.MeasureSize( value.Substring( 0, caretPos ), font, false );
|
||||
chatCaretTexture.X1 = 10 + trimmedSize.Width;
|
||||
Size charSize = Utils2D.MeasureSize( chatInputText.Substring( caretPos, 1 ), font, false );
|
||||
Size charSize = Utils2D.MeasureSize( value.Substring( caretPos, 1 ), font, false );
|
||||
chatCaretTexture.Width = charSize.Width;
|
||||
}
|
||||
size.Height = Math.Max( size.Height, chatCaretTexture.Height );
|
||||
@ -60,7 +62,7 @@ namespace ClassicalSharp {
|
||||
using( Bitmap bmp = new Bitmap( size.Width, size.Height ) ) {
|
||||
using( Graphics g = Graphics.FromImage( bmp ) ) {
|
||||
Utils2D.DrawRect( g, backColour, 0, 0, bmp.Width, bmp.Height );
|
||||
DrawTextArgs args = new DrawTextArgs( GraphicsApi, chatInputText, Color.White, false );
|
||||
DrawTextArgs args = new DrawTextArgs( GraphicsApi, value, Color.White, false );
|
||||
Utils2D.DrawText( g, font, ref args, 0, 0 );
|
||||
}
|
||||
chatInputTexture = Utils2D.Make2DTexture( GraphicsApi, bmp, 10, y );
|
||||
@ -91,9 +93,9 @@ namespace ClassicalSharp {
|
||||
}
|
||||
|
||||
public void SendTextInBufferAndReset() {
|
||||
Window.SendChat( chatInputText );
|
||||
Window.SendChat( chatInputText.ToString() );
|
||||
typingLogPos = Window.ChatInputLog.Count; // Index of newest entry + 1.
|
||||
chatInputText = "";
|
||||
chatInputText.Clear();
|
||||
Dispose();
|
||||
}
|
||||
|
||||
@ -149,9 +151,9 @@ namespace ClassicalSharp {
|
||||
public override bool HandlesKeyPress( char key ) {
|
||||
if( chatInputText.Length < 64 && !IsInvalidChar( key ) ) {
|
||||
if( caretPos == -1 ) {
|
||||
chatInputText += key;
|
||||
chatInputText.Append( chatInputText.Length, key );
|
||||
} else {
|
||||
chatInputText = chatInputText.Insert( caretPos, new String( key, 1 ) );
|
||||
chatInputText.InsertAt( caretPos, key );
|
||||
caretPos++;
|
||||
}
|
||||
Dispose();
|
||||
@ -162,14 +164,14 @@ namespace ClassicalSharp {
|
||||
}
|
||||
|
||||
void BackspaceKey() {
|
||||
if( chatInputText.Length > 0 ) {
|
||||
if( !chatInputText.Empty ) {
|
||||
if( caretPos == -1 ) {
|
||||
chatInputText = chatInputText.Remove( chatInputText.Length - 1, 1 );
|
||||
chatInputText.DeleteAt( chatInputText.Length - 1 );
|
||||
Dispose();
|
||||
Init();
|
||||
} else if( caretPos > 0 ) {
|
||||
caretPos--;
|
||||
chatInputText = chatInputText.Remove( caretPos, 1 );
|
||||
chatInputText.DeleteAt( caretPos );
|
||||
Dispose();
|
||||
Init();
|
||||
}
|
||||
@ -177,8 +179,8 @@ namespace ClassicalSharp {
|
||||
}
|
||||
|
||||
void DeleteKey() {
|
||||
if( chatInputText.Length > 0 && caretPos != -1 ) {
|
||||
chatInputText = chatInputText.Remove( caretPos, 1 );
|
||||
if( !chatInputText.Empty && caretPos != -1 ) {
|
||||
chatInputText.DeleteAt( caretPos );
|
||||
if( caretPos >= chatInputText.Length ) caretPos = -1;
|
||||
Dispose();
|
||||
Init();
|
||||
@ -186,7 +188,7 @@ namespace ClassicalSharp {
|
||||
}
|
||||
|
||||
void RightKey() {
|
||||
if( chatInputText.Length > 0 && caretPos != -1 ) {
|
||||
if( !chatInputText.Empty && caretPos != -1 ) {
|
||||
caretPos++;
|
||||
if( caretPos >= chatInputText.Length ) caretPos = -1;
|
||||
Dispose();
|
||||
@ -195,7 +197,7 @@ namespace ClassicalSharp {
|
||||
}
|
||||
|
||||
void LeftKey() {
|
||||
if( chatInputText.Length > 0 ) {
|
||||
if( !chatInputText.Empty ) {
|
||||
if( caretPos == -1 ) caretPos = chatInputText.Length;
|
||||
caretPos--;
|
||||
if( caretPos < 0 ) caretPos = 0;
|
||||
@ -208,7 +210,8 @@ namespace ClassicalSharp {
|
||||
if( Window.ChatInputLog.Count > 0 ) {
|
||||
typingLogPos--;
|
||||
if( typingLogPos < 0 ) typingLogPos = 0;
|
||||
chatInputText = Window.ChatInputLog[typingLogPos];
|
||||
chatInputText.Clear();
|
||||
chatInputText.Append( 0, Window.ChatInputLog[typingLogPos] );
|
||||
caretPos = -1;
|
||||
Dispose();
|
||||
Init();
|
||||
@ -218,11 +221,11 @@ namespace ClassicalSharp {
|
||||
void DownKey() {
|
||||
if( Window.ChatInputLog.Count > 0 ) {
|
||||
typingLogPos++;
|
||||
chatInputText.Clear();
|
||||
if( typingLogPos >= Window.ChatInputLog.Count ) {
|
||||
typingLogPos = Window.ChatInputLog.Count;
|
||||
chatInputText = "";
|
||||
} else {
|
||||
chatInputText = Window.ChatInputLog[typingLogPos];
|
||||
chatInputText.Append( 0, Window.ChatInputLog[typingLogPos] );
|
||||
}
|
||||
caretPos = -1;
|
||||
Dispose();
|
||||
@ -251,17 +254,17 @@ namespace ClassicalSharp {
|
||||
}
|
||||
|
||||
if( caretPos == -1 ) {
|
||||
chatInputText += text;
|
||||
chatInputText.Append( chatInputText.Length, text );
|
||||
} else {
|
||||
chatInputText = chatInputText.Insert( caretPos, text );
|
||||
chatInputText.Append( caretPos, text );
|
||||
caretPos += text.Length;
|
||||
}
|
||||
Dispose();
|
||||
Init();
|
||||
return true;
|
||||
} else if( key == Key.C && controlDown ) {
|
||||
if( !String.IsNullOrEmpty( chatInputText ) ) {
|
||||
Clipboard.SetText( chatInputText );
|
||||
if( !chatInputText.Empty ) {
|
||||
Clipboard.SetText( chatInputText.ToString() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace ClassicalSharp {
|
||||
List<DrawTextArgs> parts = null;
|
||||
Size size = new Size( 0, defaultHeight );
|
||||
parts = Utils2D.SplitText( GraphicsApi, text, true );
|
||||
size = Utils2D.MeasureSize( Utils.StripColours( text ), font, true );
|
||||
size = Utils2D.MeasureSize( parts, font, true );
|
||||
|
||||
X = CalcAdjOffset( XOffset, Window.Width, size.Width, HorizontalDocking );
|
||||
Y = CalcAdjOffset( YOffset, Window.Height, size.Height, VerticalDocking );
|
||||
|
@ -156,6 +156,7 @@
|
||||
<Compile Include="Utils\FastColour.cs" />
|
||||
<Compile Include="Utils\TextureAtlas1D.cs" />
|
||||
<Compile Include="Utils\TextureAtlas2D.cs" />
|
||||
<Compile Include="Utils\UnsafeString.cs" />
|
||||
<Compile Include="Utils\Utils.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -39,6 +39,7 @@ namespace ClassicalSharp {
|
||||
RaiseEvent( ChatReceived, new ChatEventArgs( text, 0 ) );
|
||||
}
|
||||
|
||||
ChatEventArgs chatArgs = new ChatEventArgs( "", 0 );
|
||||
public void AddChat( string text, byte type ) {
|
||||
CpeMessageType cpeType = (CpeMessageType)type;
|
||||
if( cpeType == 0 ) {
|
||||
@ -59,7 +60,9 @@ namespace ClassicalSharp {
|
||||
} else if( cpeType == CpeMessageType.Announcement ) {
|
||||
Announcement = text;
|
||||
}
|
||||
RaiseEvent( ChatReceived, new ChatEventArgs( text, type ) );
|
||||
chatArgs.Text = text;
|
||||
chatArgs.Type = type;
|
||||
RaiseEvent( ChatReceived, chatArgs );
|
||||
}
|
||||
|
||||
const string fileNameFormat = "yyyy-MM-dd";
|
||||
|
@ -122,7 +122,7 @@ namespace ClassicalSharp {
|
||||
|
||||
public sealed class ChatEventArgs : EventArgs {
|
||||
|
||||
public readonly byte Type;
|
||||
public byte Type;
|
||||
|
||||
public string Text;
|
||||
|
||||
|
@ -24,7 +24,7 @@ namespace ClassicalSharp.Renderers {
|
||||
|
||||
using( Font font = new Font( "Arial", 14 ) ) {
|
||||
List<DrawTextArgs> parts = Utils2D.SplitText( Graphics, player.DisplayName, true );
|
||||
Size size = Utils2D.MeasureSize( Utils.StripColours( player.DisplayName ), font, true );
|
||||
Size size = Utils2D.MeasureSize( parts, font, true );
|
||||
nameTexture = Utils2D.MakeTextTexture( parts, font, size, 0, 0 );
|
||||
nameWidth = size.Width;
|
||||
nameHeight = size.Height;
|
||||
|
117
Utils/UnsafeString.cs
Normal file
117
Utils/UnsafeString.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using System;
|
||||
|
||||
namespace ClassicalSharp {
|
||||
|
||||
// Class used to minimise memory allocations of strings.
|
||||
// Really, only useful for FpsScreen and TextInputWidget as they allocate lots of very small strings.
|
||||
// Seriously, you should *not* use this anywhere else, as Empty and Length are O(N).
|
||||
internal unsafe class UnsafeString {
|
||||
|
||||
internal string value;
|
||||
internal int capacity;
|
||||
|
||||
public UnsafeString( int capacity ) {
|
||||
this.capacity = capacity;
|
||||
value = new String( '\0', capacity );
|
||||
}
|
||||
|
||||
public UnsafeString Append( int index, char c ) {
|
||||
fixed( char* ptr = value ) {
|
||||
ptr[index] = c;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public UnsafeString Append( int index, string s ) {
|
||||
fixed( char* ptr = value ) {
|
||||
char* offsetPtr = ptr + index;
|
||||
return Append( ref offsetPtr, s );
|
||||
}
|
||||
}
|
||||
|
||||
public UnsafeString Append( ref char* ptr, string s ) {
|
||||
for( int i = 0; i < s.Length; i++ ) {
|
||||
*ptr++ = s[i];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
static char[] numBuffer = new char[20];
|
||||
public UnsafeString AppendNum( ref char* ptr, long num ) {
|
||||
int index = 0;
|
||||
numBuffer[index++] = (char)( '0' + ( num % 10 ) );
|
||||
num /= 10;
|
||||
|
||||
while( num > 0 ) {
|
||||
numBuffer[index++] = (char)( '0' + ( num % 10 ) );
|
||||
num /= 10;
|
||||
}
|
||||
for( int i = index - 1; i >= 0; i-- ) {
|
||||
*ptr++ = numBuffer[i];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public UnsafeString Clear() {
|
||||
fixed( char* ptr = value ) {
|
||||
return Clear( ptr );
|
||||
}
|
||||
}
|
||||
|
||||
public UnsafeString Clear( char* ptr ) {
|
||||
for( int i = 0; i < capacity; i++ ) {
|
||||
*ptr++ = '\0';
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void DeleteAt( int index ) {
|
||||
fixed( char* ptr = value ) {
|
||||
char* offsetPtr = ptr + index;
|
||||
for( int i = index; i < capacity - 1; i++ ) {
|
||||
*offsetPtr = *( offsetPtr + 1 );
|
||||
offsetPtr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertAt( int index, char c ) {
|
||||
fixed( char* ptr = value ) {
|
||||
char* offsetPtr = ptr + capacity - 1;
|
||||
for( int i = capacity - 1; i > index; i-- ) {
|
||||
*offsetPtr = *( offsetPtr - 1 );
|
||||
offsetPtr--;
|
||||
}
|
||||
offsetPtr = ptr + index;
|
||||
*offsetPtr = c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool Empty {
|
||||
get {
|
||||
for( int i = 0; i < capacity; i++ ) {
|
||||
if( value[i] != '\0' )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int Length {
|
||||
get {
|
||||
int len = capacity;
|
||||
for( int i = capacity - 1; i >= 0; i-- ) {
|
||||
if( value[i] != '\0' )
|
||||
break;
|
||||
len--;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return value.TrimEnd( '\0' );
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user