Open DarkHarlock opened 6 months ago
Tagging subscribers to this area: @dotnet/interop-contrib See info in area-owners.md if you want to be subscribed.
Author: | DarkHarlock |
---|---|
Assignees: | - |
Labels: | `area-System.Runtime.InteropServices`, `untriaged` |
Milestone: | - |
Tagging subscribers to 'arch-wasm': @lewing See info in area-owners.md if you want to be subscribed.
Author: | DarkHarlock |
---|---|
Assignees: | - |
Labels: | `arch-wasm`, `area-System.Runtime.InteropServices`, `untriaged` |
Milestone: | - |
Running this code, just like with Raylib, the call to TestCase.GetBuffer64() method results in a System.InvalidProgramException. Is this intended to be a supported pinvoke method definition?
Yes, this is intended to be a supported pinvoke method definition. Looks like you have run into wasm-specific bug.
My subsequent question is whether this "trick" (move ret value as first parameter when sizeof(T) > sizeof(nint)) should be considered supported
This trick is not generally supported. It is non-portable code. It will happen to work for some platform and method signature combinations, but not others.
I suspect the problem is InlineArray. In this case a fixed array in the struct definition might work. I've added this feature to a tracking issue, and will try to get to it
The inline array was just used to replicate the issue in an easy way, but the original problem was from code like the following from Raylib:
typedef struct Font {
int baseSize; // Base size (default chars height)
int glyphCount; // Number of glyph characters
int glyphPadding; // Padding around the glyph characters
Texture2D texture; // Texture atlas containing the glyphs
Rectangle *recs; // Rectangles in texture for the glyphs
GlyphInfo *glyphs; // Glyphs info data
} Font;
RLAPI Font GetFontDefault(void);
that is imported in C# with the following binding:
public struct Font
{
public int BaseSize;
public int GlyphCount;
public int GlyphPadding;
public Texture2D Texture;
public unsafe Rectangle* Recs;
public unsafe GlyphInfo* Glyphs;
}
[DllImport("raylib", CallingConvention = CallingConvention.Cdecl)]
public static extern Font GetFontDefault();
However, reading that the issue might be with the InlineArray, I updated the code to be like:
public struct Buffer64
{
private byte _element0;
private byte _element1;
private byte _element2;
...
private byte _element63;
public readonly byte this[int index]
{
get
{
switch (index)
{
case 0: return _element0;
...
case 63: return _element63;
}
throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
with no changes on C side.
In this case, instead of getting an System.InvalidProgramException
, I now encounter a runtime error and the entire Blazor host unloads. I moved again on the original Raylib project to verify what of the 2 error I get, and it was runtime error, the same that I get without the InlineArray.
Sorry, with the initial tests, I stopped at the point that it didn't work in both cases, not realizing that the kind of error was different.
As a recap:
Note that the trick of passing it as ref on first argument still works with both struct types.
Thanks for testing more. We'll look into this too.
Is Texture2D a struct with members? Or is it an alias for a pointer/scalar
Is Texture2D a struct with members? Or is it an alias for a pointer/scalar
Texture2d is defined as a struct with members:
// Texture, tex data stored in GPU memory (VRAM)
typedef struct Texture {
unsigned int id; // OpenGL texture id
int width; // Texture base width
int height; // Texture base height
int mipmaps; // Mipmap levels, 1 by default
int format; // Data format (PixelFormat type)
} Texture;
// Texture2D, same as Texture
typedef Texture Texture2D;
Just to update on this:
Hello,
I'm currently facing a challenge while migrating code that uses Raylib from a C# .NET 8 console application to Blazor WebAssembly. Initially, everything seemed to work fine until I encountered a set of APIs (such as
Font GetFontDefault(void)
) that return "by value" structures larger than a simple pointer.On Windows, these functions seem to operate without any issues. However, the problem arises when I attempt to use them in Blazor WebAssembly.
To simplify and better understand the issue, I've created a reproducible example using a
NativeFileReference
file containing the following C code:For interfacing with the APIs, I've defined the following import:
Running this code, just like with Raylib, the call to
TestCase.GetBuffer64()
method results in aSystem.InvalidProgramException
.Is this intended to be a supported pinvoke method definition? I've searched for information on this but haven't found anything significant, aside from some limitations in delegate marshaling to native code and support for varargs.
Subsequently, I conducted another test by moving the return parameter and treating it as the first argument of the method, leaving unchanged the native code, as shown in the following example:
and then used as:
With this change, things started working, and the buffer contains the correct information.
My subsequent question is whether this "trick" (move ret value as first parameter when sizeof(T) > sizeof(nint)) should be considered supported, and so safe to be used as workaround/solution to this problem, or it is merely a coincidence related to implementation details.
Thank you for your support and help in clarifying this issue.