SFML community forums
Bindings - other languages => DotNet => Topic started by: Gleade on October 11, 2018, 02:45:28 am
-
Hi,
Version: .NET 2.4
I'm trying to simply draw text and I'm wondering if anyone else is experiencing this issue. The issue is that the text isn't displaying correctly, it's missing characters and has strange gaps in between characters.
What is interesting is I'm getting similar but different results per-run of my application. I have not attempted to replicate the problem, but I will if no one else has or is experiencing this issue.
Trying to draw "Loading: 100%"
Run 1 (Notice the huge gap):
(https://i.imgur.com/G0nNk9R.png)
Run 2 (Notice the missing "1"):
(https://i.imgur.com/upuMMsm.png)
Trying to draw "Hello, World" (Not sure what's going on here):
(https://i.imgur.com/bRcex8X.png)
-
Can you post a snippet of your code please? I haven't experienced anything like this when working with text objects and it seems interesting.
-
Hello,
Thank you for your interest. Ive created an engine and it wraps alot of sfml's functionality. I'm at work at the moment, but I'll happily try and recreate what I'm doing in my engine in a complete minimal example afterwards.
-
I think I found the problem, I'm loading the font from a resource (as byte data). The problem does not occur if I load the font from a local file. Also note: It only appears to occur if the text is changing.
Code below that reproduces my bug:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SFML;
using SFML.Graphics;
namespace MInimalExample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press ESC key to close window");
var window = new SimpleWindow();
window.Run();
Console.WriteLine("All done");
}
}
class SimpleWindow
{
public void Run()
{
Font font = new Font(Properties.Resources.FLAPHEAD);
Text text = new Text("Hello, World", font);
var mode = new SFML.Window.VideoMode(800, 600);
var window = new SFML.Graphics.RenderWindow(mode, "SFML works!");
window.Closed += Window_Closed;
// Start the game loop
while (window.IsOpen)
{
window.Clear();
// Process events
window.DispatchEvents();
text.DisplayedString = $"Hi {DateTime.Now}";
window.Draw(text);
// Finally, display the rendered frame on screen
window.Display();
}
}
private void Window_Closed(object sender, EventArgs e)
{
var window = (SFML.Window.Window)sender;
window.Close();
}
}
}
-
This problem may occur if the byte[] data is destroyed while the font is still being used by one or more Text instances.
The Font class itself doesn't ensure that the given byte array remains in memory (ie. it doesn't keep a reference to it), so is it possible that your Properties.Resources.FLAPHEAD array gets destroyed somehow?
-
Interestingly enough, if I just load from local file into a byte array, I get the same result. You can clearly see the scope in this example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SFML;
using SFML.Graphics;
namespace MInimalExample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press ESC key to close window");
var window = new SimpleWindow();
window.Run();
Console.WriteLine("All done");
}
}
class SimpleWindow
{
public void Run()
{
byte[] bytes = System.IO.File.ReadAllBytes("FLAPHEAD.TTF");
Font font = new Font(bytes);
Text text = new Text("Hello, World", font);
var mode = new SFML.Window.VideoMode(800, 600);
var window = new SFML.Graphics.RenderWindow(mode, "SFML works!");
window.Closed += Window_Closed;
// Start the game loop
while (window.IsOpen)
{
window.Clear();
// Process events
window.DispatchEvents();
text.DisplayedString = $"Hi {DateTime.Now}";
window.Draw(text);
// Finally, display the rendered frame on screen
window.Display();
}
}
private void Window_Closed(object sender, EventArgs e)
{
var window = (SFML.Window.Window)sender;
window.Close();
}
}
}
-
https://github.com/SFML/SFML.Net/blob/master/src/Graphics/Font.cs
public Font
(byte[] bytes
) : base(IntPtr
.Zero){ GCHandle pin
= GCHandle
.Alloc(bytes, GCHandleType
.Pinned); try { CPointer
= sfFont_createFromMemory
(pin
.AddrOfPinnedObject(), Convert
.ToUInt64(bytes
.Length)); } finally { pin
.Free(); } if (CPointer
== IntPtr
.Zero) { throw new LoadingFailedException
("font"); }}
Doesn't that code pin the byte array (to allow taking it's address), open and then (no matter what since it's in a finally block) unpin it, leaving it free to be moved by the GC when it compacts the heap?
-
Doesn't that code pin the byte array (to allow taking it's address), open and then (no matter what since it's in a finally block) unpin it, leaving it free to be moved by the GC when it compacts the heap?
I guess this is a possible explanation. Unfortunately I don't have a deep understanding of what happens here with memory.
-
This is what the description of that option sounds like: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.gchandletype?view=netframework-4.7.2
It also makes sense for why that constructor makes a 'bad' Font object no matter where the bytes[] comes from and why pinning is a thing.
Can be easily checked too I guess? Pin the bytes[] and print its address from time to time then free it (the GC pin handle) instantly, then bugs should start once new glyps and info is read after a GC move?
-
Thanks for that FRex, that looks to be the case. For a work-around, I can confirm that you can use streams instead (FileStream / MemoryStream).
(https://i.imgur.com/nIwBqFZ.png)
-
Should I open an issue about this, Laurent?
-
Sure :)
-
https://github.com/SFML/SFML.Net/issues/156