Comments (28)
Sorry, in fact I can't reproduce your problem under macOS. And I don't know when will I find a Windows machine to debug. /cc @NuriYuri Is your machine Windows?
from sfml.js.
Sorry, in fact I can't reproduce your problem under macOS. And I don't know when will I find a Windows machine to debug. /cc Is your machine Windows?
I don't know if said person still active. But I think I could debug this if you could tell me how to start.
If you're using visual studio, I think you may refer to this doc: https://computer-vision-talks.com/how-to-debug-nodejs-addons-in-visual-studio/
Then you'll be able to debug by using breakpoints.
from sfml.js.
I've tested some version of the code:
const { readFileSync } = require('fs');
const { RenderWindow, VideoMode, Text, Font } = require('sfml.js');
async function asyncRender() {
let i = 0;
const font = new Font();
const window = new RenderWindow(new VideoMode(400, 400), "issue");
// window.setVerticalSyncEnabled(true);
const data = readFileSync("C:/Windows/Fonts/arial.ttf");
font.loadFromMemory(data);
const text = new Text("Testing", font, 13);
const text2 = new Text("wololo", font, 13);
text2.setPosition(0, 26);
text.setFillColor(255); // Black
text2.setFillColor(255);
while(true) {
let event;
while ((event = window.pollEvent())) {
if (event.type === "Closed") {
window.close();
return;
}
}
i = (i + 1) % ("Testing".length)
text.setString("Testing".substr(0, i));
text2.setString(i.toString());
window.clear(-1); // White eraser
await window.drawAsync(text);
await window.drawAsync(text2);
await window.displayAsync();
}
}
(async () => {
await asyncRender();
})();
This one is a bit more stable but still gets garbage collected at some point (seems kind of random)...
from sfml.js.
from sfml.js.
New discovery:
global.fontData = readFileSync // bla bla
let font = //... Font
Setting the fontData Buffer to global works in async.
In the meantime, users can use "Font Manager" like this:
global.fontManager = {
"Segoe UI": readFileSync(...)
};
from sfml.js.
Probably not.
I did another test today. It seems replacing:
let data = readFileSync("C:/Windows/Fonts/segoeui.ttf"); font.loadFromMemory(data);with
font.loadFromFileSync("C:/Windows/Fonts/segoeui.ttf");fixes the issue on my side (no invalid glyph).
Also doing the loadFromMemory inside the asyncRender function seem to work a bit more reliably than doing it outside.
This probably means there's an issue with
loadFromMemory
because the pointer to the data we loaded from file might have been freed. Here's what the documentation says:SFML calls
FT_Done_Face
on failure or in Font::cleanup() (destructor or loading a new font).Edit: I did something really stupid. Calling
data.toString();
inside asyncRender and it has the same effect, fixes the glyph bug. I would assume data is GCd if no longer used. We probably need to be carefull with buffers when we load from memory and keep a reference to the buffer somewhere ;)
So I think we can do some modify in LoadFromMemory
. First add a Global<Value> _related_buffer
in Font
, and each time we call LoadFromMemory
, we do _related_buffer.Reset()
to set the input buffer as a global handler (for never being GCed until Font
itself's GCed). And each time we call other LoadFrom*
, just reset _related_buffer
to empty to unbind.
Refs: https://github.com/nodejs/nan/blob/main/doc/persistent.md#api_nan_global
I'm not sure will this solve the problem.
from sfml.js.
I tried something, it seems to no longer glitch with it but I'm not a v8 expert ^^
from sfml.js.
However, sync actions like this did not:
const { readFileSync } = require('fs');
const { RenderWindow, VideoMode, Text, Font } = require('sfml.js');
let i = 0;
let font = new Font();
let window = new RenderWindow(new VideoMode(400, 400), "issue");
let data = readFileSync("C:/Windows/Fonts/segoeui.ttf");
font.loadFromMemory(data);
let text = new Text("Testing", font);
text.setFillColor(255); // Black
while(true) {
let event;
while ((event = window.pollEvent())) {
if (event.type === "Closed") {
window.close();
return;
}
}
text.setString("Testing".substr(0, i++ % ("Testing".length)));
window.clear(-1); // White eraser
window.draw(text);
window.display();
}
from sfml.js.
Does it crash only when drawing Text
? How about other drawables?
from sfml.js.
from sfml.js.
Sorry, in fact I can't reproduce your problem under macOS. And I don't know when will I find a Windows machine to debug. /cc Is your machine Windows?
I don't know if said person still active. But I think I could debug this if you could tell me how to start.
from sfml.js.
Hi,
I just downloaded the preview 45 and run the async version (snippet from first message).
The thing runs at 1400FPS on my side but I've noticed that at some point the text rendering stops being good and sometimes it crashes.
It looks like the objects gets garbage collected (or at least the memory of the font is being freed because it has wrong glyph display).
from sfml.js.
I've tested some version of the code:
const { readFileSync } = require('fs'); const { RenderWindow, VideoMode, Text, Font } = require('sfml.js'); async function asyncRender() { let i = 0; const font = new Font(); const window = new RenderWindow(new VideoMode(400, 400), "issue"); // window.setVerticalSyncEnabled(true); const data = readFileSync("C:/Windows/Fonts/arial.ttf"); font.loadFromMemory(data); const text = new Text("Testing", font, 13); const text2 = new Text("wololo", font, 13); text2.setPosition(0, 26); text.setFillColor(255); // Black text2.setFillColor(255); while(true) { let event; while ((event = window.pollEvent())) { if (event.type === "Closed") { window.close(); return; } } i = (i + 1) % ("Testing".length) text.setString("Testing".substr(0, i)); text2.setString(i.toString()); window.clear(-1); // White eraser await window.drawAsync(text); await window.drawAsync(text2); await window.displayAsync(); } } (async () => { await asyncRender(); })();This one is a bit more stable but still gets garbage collected at some point (seems kind of random)...
If it was GCed, we should find that and make it a Global<>
handler before async displaying done.
from sfml.js.
I unfortunately have no clue about this Global handler :/
I also noticed that await window.drawAsync
is adding a ton of overhead, it's better calling window.draw instead since this operation cost way more than window.display (if VSYNC is on, if you don't use VSYNC, don't use async at all).
from sfml.js.
I think in the problem code which calls drawAsync
, all the text
and font
objects are at the outer scope of the clojure. I don't know why V8 has GCed them.
from sfml.js.
Maybe we can add a --trace-gc
commandline argument to trace the GC actions in Node.js. e.g.
$ node --trace-gc xxx.js
Or add some printf
at the GCed object's destructure function. e.g.
Font::~Font() {
printf("The font object is up to be destructed.\n");
if (_font != nullptr) {
delete _font;
_font = nullptr;
}
}
from sfml.js.
--trace-gc shows something like this so far:
I'll test with the printf but now I have to go.
from sfml.js.
--trace-gc shows something like this so far:
I'll test with the printf but now I have to go.
For that code, I can't imagine which object could be GCed because I think all the objects will be active in the while loop.
from sfml.js.
Indeed, I added some logs inside the async worker and the pointer to the drawable is invariant.
I've looked at something in src\drawable\text.cc
When I replace the line 88 raw.setString(text->string());
to raw.setString(sf::String("something"));
then it never fails :/
I've tested a lot of things in that function and the string is always correct so I can't really figure out what happen between this function call and the display() call that makes the memory so inconsistant, maybe we need to use the mutex in the setString() function?
from sfml.js.
Regarding some other tests I've done, it seems that for some reason the DisplayAsync is not getting proper data at some point (since it's buggy with just draw too).
The thing I can't really understand is why I have no issue when I do this thing with Ruby but with NodeJS it's not quite stable :/
from sfml.js.
For reference, in my other project (in Ruby) that's the function setting the text:
https://gitlab.com/pokemonsdk/litergss2/-/blob/development/ext/LiteRGSS/Text.cpp#L288
And that's the function rendering the whole scene:
https://gitlab.com/pokemonsdk/litergss2/-/blob/development/ext/LiteRGSS/DisplayWindowInput.cpp#L223
The biggest difference with nodeJS here is that the whole clear() => draw() => draw() => ... => display() is performed in the same thread. (The actual draw function: https://gitlab.com/NuriYuri/litecgss/-/blob/development/src/src/LiteCGSS/Views/DisplayWindow.cpp#L133 )
from sfml.js.
Indeed, I added some logs inside the async worker and the pointer to the drawable is invariant.
I've looked at something in
src\drawable\text.cc
When I replace the line 88raw.setString(text->string());
toraw.setString(sf::String("something"));
then it never fails :/I've tested a lot of things in that function and the string is always correct so I can't really figure out what happen between this function call and the display() call that makes the memory so inconsistant, maybe we need to use the mutex in the setString() function?
That means the text
has been GCed? Or just string()
method failed?
from sfml.js.
Nothing got GCd according to my later searches.
I just remembered that SFML computes the text vertices inside the draw function call if and only if the text mutates (setting the same text does not constitute a mutation).
It looks like the display() function is not receiving the right data (probably some kind of intermediary state while the draw function did process the new vertices but the vertices are still in L2/L3 cache and weren't pushed to RAM).
For now I can suggest strategy to avoid this kind of issue if only text is affected: using renderTextures. You do all the synchronous drawing stuff in render textures, and display the renderTexture asynchronously. renderTexture is stored in GPU memory so it should not have issues unless the textureID change when we rerender a renderTexture (which would be unfortunate).
from sfml.js.
Nothing got GCd according to my later searches.
I just remembered that SFML computes the text vertices inside the draw function call if and only if the text mutates (setting the same text does not constitute a mutation).
It looks like the display() function is not receiving the right data (probably some kind of intermediary state while the draw function did process the new vertices but the vertices are still in L2/L3 cache and weren't pushed to RAM).
For now I can suggest strategy to avoid this kind of issue if only text is affected: using renderTextures. You do all the synchronous drawing stuff in render textures, and display the renderTexture asynchronously. renderTexture is stored in GPU memory so it should not have issues unless the textureID change when we rerender a renderTexture (which would be unfortunate).
How about make raw
in Text
immutable? Each time we are up to change it's string, we replace the whole raw
pointer. e.g.
private:
unique_ptr<sf::Text> _raw;
from sfml.js.
That looks a bit expensive in term of change and computing processing (reallocating sf::Text object each time we change the string). I tried to do it the application straight crashed :/
I'm taking a look with the debugger,
In AsyncRenderWindowDraw:
In setText:
Next iteration:
In AsynRenderWindowDraw:
In setText:
The sf::String stuff looks pretty efficient in storing the data since the only thing that change is the size (which match our code).
When we look at the bounds, we have quite incorrect data, I would suspect the font not being right but in my previous tests I've seen that it keeps being right in memory. I don't get what's going wrong during the draw() operation of the Text...
I would say the issue stands inside the draw function, something forces it to have wrong data. For me it's quite hard to have a crash (probably because I'm full AMD powered) but at least I see weird things. I'll check later inside the SFML internals to see if it's actually computing the right data. For now I have other stuff to do ^^
from sfml.js.
News report, I'm now using much hardcore tools because VS is not a big help when it doesn't have the source code.
I can confirm that drawAsync is actually receiving the right characters :)
So if we have an issue it has to come from the font, receiving invalid glyphs, and when we look at it, most of the glyph have an advance of 0 and one of them as one of 513 (all kernings seemed to be 0 but I didn't really checked for all of them since it's the space between letters).
The issue could actually be related to this: https://en.sfml-dev.org/forums/index.php?topic=16597.msg119344#msg119344 but it could also be something else.
from sfml.js.
hmmmm. so it seems a bug (or feature) of sfml itself?
from sfml.js.
Probably not.
I did another test today. It seems replacing:
let data = readFileSync("C:/Windows/Fonts/segoeui.ttf");
font.loadFromMemory(data);
with
font.loadFromFileSync("C:/Windows/Fonts/segoeui.ttf");
fixes the issue on my side (no invalid glyph).
Also doing the loadFromMemory inside the asyncRender function seem to work a bit more reliably than doing it outside.
This probably means there's an issue with loadFromMemory
because the pointer to the data we loaded from file might have been freed. Here's what the documentation says:
SFML calls FT_Done_Face
on failure or in Font::cleanup() (destructor or loading a new font).
Edit: I did something really stupid. Calling data.toString();
inside asyncRender and it has the same effect, fixes the glyph bug. I would assume data is GCd if no longer used. We probably need to be carefull with buffers when we load from memory and keep a reference to the buffer somewhere ;)
from sfml.js.
Related Issues (10)
- Hi, I got an error when I tried to install a package HOT 1
- A logo is needed
- Support M1 Mac architecture HOT 1
- Make SFML dynamic libraries be downloaded on `npm install` time HOT 1
- Question: How to make a custom drawable? HOT 1
- `CONTRIBUTING.md` guide is needed
- Unit test structure is needed
- Documentation is needed HOT 1
- 为什么选择基于sfml? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from sfml.js.