-
-
Notifications
You must be signed in to change notification settings - Fork 966
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Native] - Implement some existing C++ methods in C# #1896
Conversation
Thanks for the PR. |
If I remember correctly, the 'FastTextRenderer' is only used for the debug text. Is this correct? |
Hey @ly3027929699 thanks a bunch for testing this out for us, can you share the benchmark source as well ? |
here is the source |
the folder is empty, could you make a repo instead? |
Nah, it works fine, make sure to change it to rar |
is your result similar to me? @Eideren |
https://github.com/ly3027929699/TestNativeVSCharp |
Fixed the benchmark's usage of unsafe, results are still significantly better for c# though, even under dotnet6
Thanks a lot @ly3027929699 for taking the time to prepare this benchmark ! |
public unsafe void GraphicsFastTextRendererGenerateVertices(RectangleF constantInfos, RectangleF renderInfos, string textPointer, ref int textLength, IntPtr vertexBufferPointer) | ||
{ | ||
var vertexBuffer = (VertexPositionNormalTexture**)vertexBufferPointer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use VertexPositionNormalTexture**
as the parameter type instead of IntPtr
, it'll be clearer for the caller and you can move the type conversion outside the loop, it's likely already inlined by the jit but might as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually incorrect as written (and crashes when enabling the Profiler for example). It should just be VertexPositionNormalTexture*
as I see it.
I suggest something like
private int GraphicsFastTextRendererGenerateVertices(RectangleF constantInfos, RectangleF renderInfos, string text, Span<VertexPositionNormalTexture> vertexBuffer)
or maybe
private int GraphicsFastTextRendererGenerateVertices(RectangleF constantInfos, RectangleF renderInfos, string text, ref VertexPositionNormalTexture vertexBuffer)
and iterating with vertexBuffer = ref Unsafe.Add(ref vertexBuffer, 1)
instead of pointer arithmetic if that's simpler/faster than Span
. I see no reason to keep a C-like signature.
protected override unsafe void UpdateBufferValuesFromElementInfo(ref ElementInfo elementInfo, IntPtr vertexPtr, IntPtr indexPtr, int vertexOffset) | ||
{ | ||
var vertex = (VertexPositionColorTextureSwizzle*)vertexPtr; | ||
fixed (SpriteDrawInfo* drawInfo = &elementInfo.DrawInfo) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here with VertexPositionColorTextureSwizzle*
if possible
Here's the result of another benchmark following the suggestions @froce made
Where
public static unsafe void Optimized(RectangleF constantInfos, RectangleF renderInfos, string textPointer, ref int textLength, Span<VertexPositionNormalTexture> vertexBuffer)
{
float fX = renderInfos.X / renderInfos.Width;
float fY = renderInfos.Y / renderInfos.Height;
float fW = constantInfos.X / renderInfos.Width;
float fH = constantInfos.Y / renderInfos.Height;
RectangleF destination = new(fX, fY, fW, fH);
RectangleF source = new(0.0f, 0.0f, constantInfos.X, constantInfos.Y);
// Copy the array length (since it may change during an iteration)
int textCharCount = textLength;
float scaledDestinationX;
float scaledDestinationY = -(destination.Y * 2f - 1f);
float invertedWidth = 1f / constantInfos.Width;
float invertedHeight = 1f / constantInfos.Height;
Span<(Vector2 Position, Vector2 TextureCoordinate)> baseData = stackalloc (Vector2, Vector2)[4]
{
( new(-destination.Width, +destination.Height), new(0 * source.Width * invertedWidth, 0 * source.Height * invertedHeight) ),
( new(+destination.Width, +destination.Height), new(1 * source.Width * invertedWidth, 0 * source.Height * invertedHeight) ),
( new(-destination.Width, -destination.Height), new(0 * source.Width * invertedWidth, 1 * source.Height * invertedHeight) ),
( new(+destination.Width, -destination.Height), new(1 * source.Width * invertedWidth, 1 * source.Height * invertedHeight) ),
};
int j = 0;
for (int i = 0; i < textCharCount; i++)
{
char currentChar = textPointer[i];
if (currentChar == '\v')
{
// Tabulation
destination.X += 8 * fX;
--textLength;
continue;
}
else if (currentChar >= 10 && currentChar <= 13) // '\n' '\v' '\f' '\r'
{
destination.X = fX;
destination.Y += fH;
scaledDestinationY = -(destination.Y * 2f - 1f);
--textLength;
continue;
}
else if (currentChar < 32 || currentChar > 126)
{
currentChar = ' ';
}
source.X = (currentChar % 32 * constantInfos.X) * invertedWidth;
source.Y = (currentChar / 32 % 4 * constantInfos.Y) * invertedHeight;
scaledDestinationX = destination.X * 2f - 1f;
// 0
vertexBuffer[j].Position.X = scaledDestinationX + baseData[0].Position.X;
vertexBuffer[j].Position.Y = scaledDestinationY + baseData[0].Position.Y;
vertexBuffer[j].TextureCoordinate.X = source.X + baseData[0].TextureCoordinate.X;
vertexBuffer[j].TextureCoordinate.Y = source.Y + baseData[0].TextureCoordinate.Y;
j++;
// 1
vertexBuffer[j].Position.X = scaledDestinationX + baseData[1].Position.X;
vertexBuffer[j].Position.Y = scaledDestinationY + baseData[1].Position.Y;
vertexBuffer[j].TextureCoordinate.X = source.X + baseData[1].TextureCoordinate.X;
vertexBuffer[j].TextureCoordinate.Y = source.Y + baseData[1].TextureCoordinate.Y;
j++;
// 2
vertexBuffer[j].Position.X = scaledDestinationX + baseData[2].Position.X;
vertexBuffer[j].Position.Y = scaledDestinationY + baseData[2].Position.Y;
vertexBuffer[j].TextureCoordinate.X = source.X + baseData[2].TextureCoordinate.X;
vertexBuffer[j].TextureCoordinate.Y = source.Y + baseData[2].TextureCoordinate.Y;
j++;
// 3
vertexBuffer[j].Position.X = scaledDestinationX + baseData[3].Position.X;
vertexBuffer[j].Position.Y = scaledDestinationY + baseData[3].Position.Y;
vertexBuffer[j].TextureCoordinate.X = source.X + baseData[3].TextureCoordinate.X;
vertexBuffer[j].TextureCoordinate.Y = source.Y + baseData[3].TextureCoordinate.Y;
j++;
destination.X += destination.Width;
}
} |
@@ -209,14 +217,13 @@ public unsafe void End([NotNull] GraphicsContext graphicsContext) | |||
|
|||
//Draw the strings | |||
var constantInfos = new RectangleF(GlyphWidth, GlyphHeight, DebugSpriteWidth, DebugSpriteHeight); | |||
Span<VertexPositionNormalTexture> vertexPositionSpan = stackalloc VertexPositionNormalTexture[4* textInfo.Text.Length]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic expects mappedVertexBufferPointer
to receive the data, you should create a span from that, so something like new Span<VertexPositionNormalTexture>(mappedVertexBufferPointer, amountOfItemsTheBufferContains);
Also, stackalloc
should be used with care, I used it in the benchmark as it wasn't that important there, but if you don't know in advance how much bytes you're going to allocate (the string in this case might be very long), you may be filling the thread's stack resulting in a StackOverflowException
see stackalloc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, Thanks everyone for your help 😀. @Eideren Thanks, I've updated my branch now. When it comes to amountOfItemsTheBufferContains do you had in mind something like in line 214? (Unsafe.InitBlock..)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost, that's the amount of bytes, new Span<T>(void*, int)
's second argument is the amount of T
or items in the collection, see here. So it should just be new((void*)mappedVertexBufferPointer, VertexBufferLength)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh my 🤦 You're right. Updated now 😀
11df0cc
to
a33e914
Compare
Thanks ! |
PR Details
Description
This PR focuses on migrating most of the C++ code in Stride.Native to C#.
My change also applies to Stride.Graphics, so with a few gimmicks it should be possible to build this library outside of Windows.
Related Issue
#1394
Types of changes
TODO