root icon indicating copy to clipboard operation
root copied to clipboard

[reve]Draw text with SDF fonts in screen or world coordinates

Open alja opened this issue 1 year ago • 20 comments

This Pull request:

Add functionality to draw Scale Distance Field Fonts in the screen or world coordinates.

Example:

/tutorials/eve7/texts.C

alja avatar Jun 10 '24 22:06 alja

This change is related to development in RenderCore https://github.com/UL-FRI-LGM/RenderCore/pull/20

alja avatar Jun 10 '24 22:06 alja

@linev @osschar Please check.

alja avatar Jun 10 '24 22:06 alja

tutorials/eve7/texts.C screenshot

Screenshot 2024-06-10 at 3 41 35 PM

alja avatar Jun 10 '24 22:06 alja

Test Results

    13 files      13 suites   3d 3h 5m 0s :stopwatch:  3 021 tests  3 021 :white_check_mark: 0 :zzz: 0 :x: 33 762 runs  33 762 :white_check_mark: 0 :zzz: 0 :x:

Results for commit 6dcb57a3.

:recycle: This comment has been updated with latest results.

github-actions[bot] avatar Jun 10 '24 23:06 github-actions[bot]

Sergey, all good questions, thank you :)

  1. Do we need to include all these fonts into ROOT repository? Font files are big and used only via random generator in tutorial.

No, we can remove most of them, rebase and force-push to the branch.

  1. How to deal with other fonts? Maybe one can include one default font in ui5/eve7/ folder, one or two fonts as examples in tutorials and provide recipe where other fonts can be found?

Yes, we could do that. Font textures and metrics files are generated from TTF via https://github.com/osschar/sdf_atlas. It's a small tool, easy to build on linux -- so we could provide instructions for this and also a catalog/web-site with a bunch of pre-generated fonts.

  1. If I understand correct, REveText rendering will be only supported with RenderCore? That about three.js? Beside fonts support it is easy to implement text rendering there.

Yes, I know ... they have a monster implementation(s) of fonts. Seeing that I went looking for something super simple and still good looking :)

Now, this is a bit unfortunate ... but I don't think I have the bandwidth to keep Three fully supported. Also, the low-level, renderer- and shader-level support we are getting from RenderCore (in particular, for picking & rendering of instanced objects and the upcoming spline-based line rendering) is making it possible to support features and performance optimizations that I do not think would be doable in Three with the time budget we all have and the level of changes we can do in core Three (zero, unless we can hack over it locally).

  1. Is it possible to support other font formats - like TTF or OTF? ROOT already includes such fonts, is it an option?

This would be nice, sdf_atlas could be incorporated into root (it requires minimal GL support which we already have) -- and one could then generate the missing SDF fonts during the startup of a demo/application. License is free to use in whatever way, just keep the copyright notice. But it only supports TTF, not OTF. Perhaps we should look for another library ... or at least a new TTF/OTF parser -- I don't know much about low-level font things so this maybe already exists in root?.

I understand this is not all perfect -- but at this point our priority is to get something usable in for the existing REve applications.

osschar avatar Jun 11 '24 21:06 osschar

but at this point our priority is to get something usable in for the existing REve applications.

Then I propose to reduce number of fonts - one or two for now.

And think how one can allow to use external fonts - in this PR or may be later.

linev avatar Jun 12 '24 06:06 linev

@linev I will add today the change to reduce the number of fonts. Just Serif and Mono Space type fonts in only regular style (no italic and bold).

alja avatar Jun 12 '24 17:06 alja

@linev I have pushed changes. I have rebased to master and squashed commits to reduce the load on git repository. There are some pending changes from Matevz. Please wait for his commit.

alja avatar Jun 13 '24 17:06 alja

OK, I force-pushed a new version, now there are no SDF fonts in the repo -- but I have added two ttf fonts that have all the accented and greek characters.

The creator of sdf font stuff is in graf3d/gl as it requires open-gl for rendering of the glyphs. texts.C demo will now make sure the fonts are available in the web server repo under ui5/eve7/fonts/.

There is only one thing I couldn't figure out -- how to link against libpng ... so I just added -lpng into cmake file for graf3d/gl/. Please help me figure out how to do this correctly.

osschar avatar Jun 14 '24 20:06 osschar

Sergey, semi-related, have been running with valgrind (root supressions on), and this came up:

root [1] ==975849== Thread 8 civetweb-worker:
==975849== Conditional jump or move depends on uninitialised value(s)
==975849==    at 0x4CD6584: R__longest_match (ZDeflate.c:430)
==975849==    by 0x4CD6EA8: R__Deflate (ZDeflate.c:677)
==975849==    by 0x4CD6084: R__memcompress (Bits.c:329)
==975849==    by 0x22699978: THttpCallArg::CompressWithGzip() (THttpCallArg.cxx:420)
==975849==    by 0x22693E4F: begin_request_handler(mg_connection*, void*) (TCivetweb.cxx:467)
==975849==    by 0x226CCE61: handle_request (civetweb.c:14331)
==975849==    by 0x226BCF09: handle_request_stat_log (civetweb.c:6544)
==975849==    by 0x226D3421: process_new_connection (civetweb.c:18629)
==975849==    by 0x226D4038: worker_thread_run (civetweb.c:19036)
==975849==    by 0x226D416F: worker_thread (civetweb.c:19097)
==975849==    by 0x535D896: start_thread (pthread_create.c:444)
==975849==    by 0x53E48C3: clone (clone.S:100)

osschar avatar Jun 15 '24 20:06 osschar

@osschar

==975849== at 0x4CD6584: R__longest_match (ZDeflate.c:430)

It is inside zlib. Hope it is just false positive warning from valgrind.

linev avatar Jun 17 '24 06:06 linev

Yeah, they probably do some quirky simd stuff in there :)

I realized yesterday that the stuff for creation of sdf textures always puts them in $ROOTSYS/ui5/eve7/ ... which will not be writable for experiment-wide / cvmfs installs. Would it be possible to do something like this:

  1. if $ROOTSYS/ is not writable, write to ./fonts/;
  2. forward requests for files in $ROOTSYS/ui5/eve7/ to some REveManager function that can then check where these fonts are actually available and route the GET to the proper local file?

osschar avatar Jun 17 '24 06:06 osschar

There is only one thing I couldn't figure out -- how to link against libpng ... so I just added -lpng into cmake file for graf3d/gl/. Please help me figure out how to do this correctly.

Not so simple. Sometime ROOT uses system-wide libpng, sometime - builtin version included in ASImage. And they can conflict with each other. Major problem - builtin with ASImage is not available from outside.

You have to ensure that your code not linked with ASImage. And provide in your CMakeLists.txt file construct like

find_Package(PNG)
# handle include directories

Because you may need to use custom include directories. See here

linev avatar Jun 17 '24 06:06 linev

OK, I've moved png-writer to asimage.

All that remains from my side is to figure out how to handle the case when $ROOTSYS is not writable -- see my comment above: https://github.com/root-project/root/pull/15812#issuecomment-2172404599

osschar avatar Jun 18 '24 07:06 osschar

to figure out how to handle the case when $ROOTSYS is not writable

One can try to load fonts from current directory. http server uses http://server/currentdir/ alias for files from current directory. One can use it to load fonts. The only to be done - call win.SetUseCurrentDir(true); in REve initialization - because of security reasons option is by default off.

Feature available only in master since 2 days - please rebase your code

linev avatar Jun 18 '24 09:06 linev

Hmmh, but then I'd need to stream path information with every REveText object, now only the font name is streamed. It is indeed a reasonable thing to check if the font files exist before object's json is sent over to the client --- but creating a font during the streaming traversal would be pushing it a bit.

Is it possible to register custom prefixes and callbacks from REveManager (via RWebWindow) so they can be handled when requests come in? Like: http://server/sdf-fonts/

I went through the code a bit (but clearly do not have the full picture) ... one way would be to add THttpServer::fActiveLocations, where instead of replacement string one provides a lambda [](TString& prefix, TString& reminder, THttpRequest& req, THttpServer &srv) so one can then do appropriate lookup in the callback, potentially generating the font, and then calling srv->SendFile() (or sending back the default font, if the desired one can not be found/generated).

I think this functionality could be useful for other cases in REve, where semi-static data needs to be provided.

The font-generation code in REveText invokes TGL generator through the interpreter now, via gROOT->ProcessLine(), to avoid dependency of REve on RGL. Is this OK to do from a request handler thread or should cross-thread request to the main thread be made (and request told to try again in N seconds)?

osschar avatar Jun 18 '24 15:06 osschar

In my mind, procedure can be:

  1. Before starting server and display window, one creates fonts from TTF file and put them in current directory. Like REveManager::CreateFonts(....).
  2. Enable usage of current dir with win.SetUseCurrentDir(true)
  3. Font name is file name. If font name does not include path - then automatically currentdir/ is prepended.

linev avatar Jun 18 '24 16:06 linev

Hmmh, so you don't want to give me an active directory? :) This could be really useful for accessing large tables per-partes ... or pre-processed parts of geometry, you know ;)

osschar avatar Jun 18 '24 22:06 osschar

Hmmh, so you don't want to give me an active directory? :)

For the moment there is no direct support of such feature with RWebWindow. But can be provided - if really necessary.

In my mind, solution with direct access of font files via currentdir/ path is much clear. And does not require any extra threads locking.

You always can implement active directory - but does it necessary with fonts?

linev avatar Jun 19 '24 07:06 linev

OK ... done. I have used THttpServer::AddLocation() to register sdf-fonts/ to either $ROOTSYS/ui5/eve7/sdf-fonts or ./sdf-fonts. One can also specify the location manually through REveText::SetSdfFontDir().

osschar avatar Jun 20 '24 12:06 osschar

I've moved the png low-level code into libAfterImage, added docs, and added the two classes (TGLSdfFontMaker and TASPngWriter) into appropriate link-defs.

osschar avatar Jul 25 '24 08:07 osschar

Hmmh, it seems CI builds can not find the new file from graf2d/asimage/libAfterImage/:

  [ 86%] Building CXX object graf2d/asimage/CMakeFiles/ASImage.dir/src/TASPngWriter.cxx.o
  Error: /github/home/ROOT-CI/src/graf2d/asimage/src/TASPngWriter.cxx:3:10: fatal error: afterrootpngwrite.h: No such file or directory
      3 | #include <afterrootpngwrite.h>
        |          ^~~~~~~~~~~~~~~~~~~~~
  compilation terminated.

I had to do rm -rf AFTERIMAGE-prefix in my build dir to have the cmake install the new files. Is it possible the CI builds do something different here?

The F39 CI build log only has this:

  [  0%] Built target AFTERIMAGE

while mine has (grepped out AFTERIMAGE parts):

[  0%] Creating directories for 'AFTERIMAGE'
[  2%] Performing download step for 'AFTERIMAGE'
[ 10%] No update step for 'AFTERIMAGE'
[ 12%] No patch step for 'AFTERIMAGE'
[ 16%] Performing configure step for 'AFTERIMAGE'
-- AFTERIMAGE configure command succeeded.  See also /home/matevz/root-dev/dev-1-bld/AFTERIMAGE-prefix/src/AFTERIMAGE-stamp/AFTERIMAGE-configure-*.log
[ 85%] Performing build step for 'AFTERIMAGE'
-- AFTERIMAGE build command succeeded.  See also /home/matevz/root-dev/dev-1-bld/AFTERIMAGE-prefix/src/AFTERIMAGE-stamp/AFTERIMAGE-build-*.log
[ 97%] Performing install step for 'AFTERIMAGE'
-- AFTERIMAGE install command succeeded.  See also /home/matevz/root-dev/dev-1-bld/AFTERIMAGE-prefix/src/AFTERIMAGE-stamp/AFTERIMAGE-install-*.log
[ 97%] Completed 'AFTERIMAGE'
[ 97%] Built target AFTERIMAGE
[ 97%] Built target G__ASImage
[ 97%] Building CXX object graf2d/asimage/CMakeFiles/ASImage.dir/src/TASPluginGS.cxx.o
[ 97%] Building CXX object graf2d/asimage/CMakeFiles/ASImage.dir/src/TASImage.cxx.o
[ 97%] Building CXX object graf2d/asimage/CMakeFiles/ASImage.dir/src/TASPngWriter.cxx.o
[ 97%] Linking CXX shared library ../../lib/libASImage.so
[ 97%] Built target ASImage

osschar avatar Jul 25 '24 16:07 osschar

Oh, I see now ... builtin_afterimage is not used in the CI builds -- and might or might not be used in an arbitrary build. That means my changes can not go into libAfterImage after all.

osschar avatar Jul 25 '24 20:07 osschar

Vincenzo told me builtin_afterimage is always on now ... and Danilo told me to add the clean build tag. Now things seem to be building as expected on linuxen.

Something still goes wrong on macs but there is not much info in the CI log ... it says there is some more in the actual log file.

osschar avatar Jul 25 '24 21:07 osschar

I don't know how or why but AFTERIMAGE build did not run for windows and mac.

osschar avatar Jul 25 '24 23:07 osschar

Ah, checked on Alja's mac ... me R an idiot ... forgot to include config.h :)

osschar avatar Jul 25 '24 23:07 osschar

@alja @osschar

Grate! Now it compiles!

Last question. Why you include TGLSdfFontMaker into libGL? It creates dependency of libGL from libAfterImage and indirectly from many libs like libpng, libjpeg, libungif. This can be dangerous - if somebody already uses ROOT and link against libGL.

Maybe one just can create special small library within REve folder? You anyway using ProcessLine and therefore independent from where class comes from.

linev avatar Jul 26 '24 07:07 linev

Yay, thanks for your help Sergey!

I put TGLSdfFontMaker in RGL as it requires off-screen GL context to render the SDF font texture. RGL already depends on libASImage ... TGLViewer::SaveAs() calls it directly to store screenshots into png/jpg files.

Alja and I have checked it on mac and it works ok. There was an interesting side-story where libRGL got linked against /usr/X11R6/lib/libGL.so (presumably from an old install of XQuartz and libX11 stuff from homebrew -- it worked after we wiped both of those) instead of Apple's OpenGL framework.

I still want to test this on Windows, then I'll rebase and force-push so there's a single commit.

osschar avatar Jul 26 '24 12:07 osschar

Ok, if libGL already depends from libAfterImage - then it is fine.

You may add simple gtest-based code directly for libGL - then it will be automatically tested on all platforms.

linev avatar Jul 26 '24 12:07 linev

I want to see it :) Especially after the tricky issue on mac. It seemed to run fine but the font texture was just noise.

osschar avatar Jul 26 '24 13:07 osschar