Git Product home page Git Product logo

Comments (28)

XadillaX avatar XadillaX commented on May 30, 2024 1

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.

XadillaX avatar XadillaX commented on May 30, 2024 1

@XadillaX

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.

NuriYuri avatar NuriYuri commented on May 30, 2024 1

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();
})();

image

This one is a bit more stable but still gets garbage collected at some point (seems kind of random)...

from sfml.js.

HanzCEO avatar HanzCEO commented on May 30, 2024 1

from sfml.js.

HanzCEO avatar HanzCEO commented on May 30, 2024 1

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.

XadillaX avatar XadillaX commented on May 30, 2024 1

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: image

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.

NuriYuri avatar NuriYuri commented on May 30, 2024 1

I tried something, it seems to no longer glitch with it but I'm not a v8 expert ^^

from sfml.js.

HanzCEO avatar HanzCEO commented on May 30, 2024

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.

XadillaX avatar XadillaX commented on May 30, 2024

Does it crash only when drawing Text? How about other drawables?

from sfml.js.

HanzCEO avatar HanzCEO commented on May 30, 2024

from sfml.js.

HanzCEO avatar HanzCEO commented on May 30, 2024

@XadillaX

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.

NuriYuri avatar NuriYuri commented on May 30, 2024

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.

XadillaX avatar XadillaX commented on May 30, 2024

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();
})();

image

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.

NuriYuri avatar NuriYuri commented on May 30, 2024

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.

XadillaX avatar XadillaX commented on May 30, 2024

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.

XadillaX avatar XadillaX commented on May 30, 2024

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.

NuriYuri avatar NuriYuri commented on May 30, 2024

--trace-gc shows something like this so far:
image

I'll test with the printf but now I have to go.

from sfml.js.

XadillaX avatar XadillaX commented on May 30, 2024

--trace-gc shows something like this so far: image

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.

NuriYuri avatar NuriYuri commented on May 30, 2024

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.

NuriYuri avatar NuriYuri commented on May 30, 2024

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.

NuriYuri avatar NuriYuri commented on May 30, 2024

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.

XadillaX avatar XadillaX commented on May 30, 2024

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?

That means the text has been GCed? Or just string() method failed?

from sfml.js.

NuriYuri avatar NuriYuri commented on May 30, 2024

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.

XadillaX avatar XadillaX commented on May 30, 2024

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.

NuriYuri avatar NuriYuri commented on May 30, 2024

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:
image
In setText:
image

Next iteration:
In AsynRenderWindowDraw:
image
In setText:
image

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...
image

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.

NuriYuri avatar NuriYuri commented on May 30, 2024

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 :)
image

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).
image

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.

XadillaX avatar XadillaX commented on May 30, 2024

hmmmm. so it seems a bug (or feature) of sfml itself?

from sfml.js.

NuriYuri avatar NuriYuri commented on May 30, 2024

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:
image

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.