grovesNL / glyphon

🦅🦁 Fast, simple 2D text renderer for wgpu
438 stars 54 forks source link

Clipped text on multiple draws #113

Closed tjpalmer closed 2 months ago

tjpalmer commented 2 months ago

Thanks so much for the great work on glyphon! I have an issue in this code where I use the same text renderer in the same frame to draw in multiple places. The calls are driven from a separate app, and here's the code where the multiple draws originate. The left-aligned "Score" text is clipped off:

image

But if I comment out the second draw and keep only the first, it seems to render correctly:

image

Also, if I print out numbers (such as with dbg! in the Rust code), they look as expected. Any guesses what I'm doing wrong here or how to troubleshoot?

tjpalmer commented 2 months ago

For example, I added extra dbg and get printouts like this on each frame:

[native/src/text.rs:137:9] text = "Score: 800"
[native/src/text.rs:137:9] &align_x = Left
[native/src/text.rs:137:9] &align_y = Top
[native/src/text.rs:137:9] left = 10.0
[native/src/text.rs:137:9] top = 10.0
[native/src/text.rs:137:9] size = PhysicalSize {
    width: 800,
    height: 600,
}
[native/src/text.rs:137:9] text = "Time: 600"
[native/src/text.rs:137:9] &align_x = Right
[native/src/text.rs:137:9] &align_y = Top
[native/src/text.rs:137:9] left = 639.67773
[native/src/text.rs:137:9] top = 10.0
[native/src/text.rs:137:9] size = PhysicalSize {
    width: 800,
    height: 600,
}
tjpalmer commented 2 months ago

And if I change the order and draw the right-side text first then the left-side, I no longer see the right-side text, but the left-side looks fine.

grovesNL commented 2 months ago

What's probably happening is that prepare is overwriting the previous prepare'd text before it's actually submitted to the GPU.

The way prepare and render are meant to work together is roughly something like this:

So normally you'd only call a renderer's prepare once per frame, and ideally before you start a render pass. Then once you start a render pass, you'd call render once.

Obviously there are some trade-offs there though. For example, sometimes it's more convenient to use multiple renderers so you can call prepare on each of them with their own lists of text areas to draw. You could check this by having two text renderers.

tjpalmer commented 2 months ago

Thanks much for the thoughts! Is caching separate per renderer?

tjpalmer commented 2 months ago

I might also try just separate passes for each text draw. I'm not expert on gpu things, so I don't know all the pros and cons.

grovesNL commented 2 months ago

Is caching separate per renderer?

The caches are shared between renderers (e.g., TextAtlas and SwashCache are passed to prepare), but assuming they're being updated every frame sometimes it can be slightly more efficient to have one text renderer instead of two. If it's only a handful of text renderers I probably wouldn't worry about having one renderer vs multiple - you could just do whichever is more convenient.

I might also try just separate passes for each text draw. I'm not expert on gpu things, so I don't know all the pros and cons.

A few passes could be fine but generally you should try to keep your rendering to as few passes as possible. You can follow the same rough structure for everything you want to render:

For the first gather step, sometimes people like to queue objects to be drawn, or query them all somehow. The important point is that you're trying to prepare all GPU resources before you start the render pass. This page on the wgpu wiki https://github.com/gfx-rs/wgpu/wiki/Encapsulating-Graphics-Work might also be a good reference.

tjpalmer commented 2 months ago

Thanks much for the additional advice! I'll think about better structuring. Meanwhile, looks like using multiple renderers does do the trick. (And multiple passes doesn't change the described behavior.) Thanks again.