Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenGL Labels #72

Closed
wants to merge 26 commits into from
Closed

Conversation

LucasDworschak
Copy link
Contributor

No description provided.

 - added quads on future label positions that stay the same size regardless of camera position and always face the camera
- Labels are now fully done in the shader
 - added quads on future label positions that stay the same size regardless of camera position and always face the camera
- Labels are now fully done in the shader
 - separated code into nucleus / gl_engine
 - only one draw call for all labels and implemented instancing
 - svg icon below the label
 - unicode char in string to integer list (correct unicode decoding)
 - only render characters that are needed (until now we rendered chars 32-223 on the ascii table, now we can specify each char individually)
 - characters now contain an outline
 - minor code structure improvements
 - add uniform shader variable option for distance scaling
 - draw "space" character if char is not available in the font_atlas
 - occlusion culling
 - distance fadeout
 - code structure refactoring
- improved label fadeout
Copy link
Member

@adam-ce adam-ce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice changes. i'm super happy that we have much improved labels now.

in general i like the code, there are some minor code style issues detailed in the comments.

I'll push some changes to radix very soon, because currently address sanitizer is broken. they'll also enable warnings (just fix your own).

Workflow:
update your labels branch, the changes will be automatically forwarded to this pull request. this also means, that you should not mix in the changes from the vector labels (create an extra branch). then click on request review in this pull request.

In addition, AddressSanitizer shows me the following issue (i don't think it's the possible leak i mentioned below):

=================================================================
==64331==ERROR: AddressSanitizer: heap-use-after-free on address 0x7f15601c4278 at pc 0x55fa84a63e2e bp 0x7f1553d012b0 sp 0x7f1553d012a0
READ of size 1 at 0x7f15601c4278 thread T4 (QSGRenderThread)
    #0 0x55fa84a63e2d in ttSHORT /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/extern/stb_slim/src/stb_slim/stb_truetype.h:1287
    #1 0x55fa84a63e2d in stbtt_ScaleForPixelHeight /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/extern/stb_slim/src/stb_slim/stb_truetype.h:2662
    #2 0x55fa84a4dec6 in nucleus::MapLabel::create_text_meta(std::unordered_map<int, nucleus::MapLabel::CharData const, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<int const, nucleus::MapLabel::CharData const> > > const&, stbtt_fontinfo const*, std::vector<int, std::allocator<int> >&, float&) /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/nucleus/map_label/MapLabel.cpp:75
    #3 0x55fa84a4dec6 in nucleus::MapLabel::init(std::unordered_map<int, nucleus::MapLabel::CharData const, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<int const, nucleus::MapLabel::CharData const> > > const&, stbtt_fontinfo const*) /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/nucleus/map_label/MapLabel.cpp:45
    #4 0x55fa84a7ea8a in nucleus::MapLabelManager::init() /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/nucleus/map_label/MapLabelManager.cpp:94
    #5 0x55fa84a412db in gl_engine::MapLabelManager::init() /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/gl_engine/MapLabelManager.cpp:32
    #6 0x55fa849f4015 in gl_engine::Window::initialise_gpu() /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/gl_engine/Window.cpp:147
    #7 0x55fa847f3872 in TerrainRenderer::TerrainRenderer() /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/app/TerrainRenderer.cpp:37
    #8 0x55fa847dfbd5 in TerrainRendererItem::createRenderer() const /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/app/TerrainRendererItem.cpp:101
    #9 0x7f1573d6f7d0 in QQuickFramebufferObject::updatePaintNode(QSGNode*, QQuickItem::UpdatePaintNodeData*) (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Quick.so.6+0x4337d0)
    #10 0x7f1573bba81f in QQuickWindowPrivate::updateDirtyNode(QQuickItem*) (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Quick.so.6+0x27e81f)
    #11 0x7f1573bbb13b in QQuickWindowPrivate::updateDirtyNodes() (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Quick.so.6+0x27f13b)
    #12 0x7f1573bbeab3 in QQuickWindowPrivate::syncSceneGraph() (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Quick.so.6+0x282ab3)
    #13 0x7f1573d7723f  (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Quick.so.6+0x43b23f)
    #14 0x7f1573d78663  (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Quick.so.6+0x43c663)
    #15 0x7f1573d795c6  (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Quick.so.6+0x43d5c6)
    #16 0x7f15718619dc  (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Core.so.6+0x2f29dc)
    #17 0x7f15710a8ac2 in start_thread nptl/pthread_create.c:442
    #18 0x7f157113a65f  (/lib/x86_64-linux-gnu/libc.so.6+0x12665f)

0x7f15601c4278 is located 367224 bytes inside of 524288-byte region [0x7f156016a800,0x7f15601ea800)
freed by thread T4 (QSGRenderThread) here:
    #0 0x7f1574105537 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
    #1 0x55fa84a7df46 in QArrayDataPointer<char>::~QArrayDataPointer() /home/madam/bin/Qt/6.6.1/gcc_64/include/QtCore/qarraydatapointer.h:104
    #2 0x55fa84a7df46 in QByteArray::~QByteArray() /home/madam/bin/Qt/6.6.1/gcc_64/include/QtCore/qbytearray.h:521
    #3 0x55fa84a7df46 in nucleus::MapLabelManager::create_font() /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/nucleus/map_label/MapLabelManager.cpp:202
    #4 0x55fa84a7e964 in nucleus::MapLabelManager::init() /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/nucleus/map_label/MapLabelManager.cpp:91
    #5 0x55fa84a412db in gl_engine::MapLabelManager::init() /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/gl_engine/MapLabelManager.cpp:32
    #6 0x55fa849f4015 in gl_engine::Window::initialise_gpu() /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/gl_engine/Window.cpp:147
    #7 0x55fa847f3872 in TerrainRenderer::TerrainRenderer() /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/app/TerrainRenderer.cpp:37
    #8 0x55fa847dfbd5 in TerrainRendererItem::createRenderer() const /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/app/TerrainRendererItem.cpp:101
    #9 0x7f1573d6f7d0 in QQuickFramebufferObject::updatePaintNode(QSGNode*, QQuickItem::UpdatePaintNodeData*) (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Quick.so.6+0x4337d0)

previously allocated by thread T4 (QSGRenderThread) here:
    #0 0x7f1574105887 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x7f15717e1d34 in QArrayData::allocate(QArrayData**, long long, long long, long long, QArrayData::AllocationOption) (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Core.so.6+0x272d34)

Thread T4 (QSGRenderThread) created by T0 here:
    #0 0x7f15740a9685 in __interceptor_pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:216
    #1 0x7f157186144c in QThread::start(QThread::Priority) (/home/madam/bin/Qt/6.6.1/gcc_64/lib/libQt6Core.so.6+0x2f244c)

SUMMARY: AddressSanitizer: heap-use-after-free /home/madam/Documents/work/tuw/alpinemaps/renderer_labels/extern/stb_slim/src/stb_slim/stb_truetype.h:1287 in ttSHORT
Shadow bytes around the buggy address:
  0x0fe32c0307f0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0fe32c030800: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0fe32c030810: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0fe32c030820: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0fe32c030830: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0fe32c030840: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd[fd]
  0x0fe32c030850: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0fe32c030860: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0fe32c030870: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0fe32c030880: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0fe32c030890: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==64331==ABORTING


namespace nucleus::map_label {
#include "stb_slim/stb_truetype.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace with struct stbtt_fontinfo; and move the import to the cpp file

{
// create the final font bitmap (3 channels -> 1 for font; 1 for outline; the last channel is empty)
const int channels = 3;
m_font_bitmap = new uint8_t[m_font_atlas_size.width() * m_font_atlas_size.height() * channels];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please no raw new and delete. these are very C like. use RAII, which is how you should do things in C++ :)

a good replacement would be std::vector<uint8_t> + resize()

or, in this case, a nucleus::Raster<uint8_t> (you would have to add a resize and fill method)

const int channels = 3;
m_font_bitmap = new uint8_t[m_font_atlas_size.width() * m_font_atlas_size.height() * channels];
// set everything to 0
memset(m_font_bitmap, 0, m_font_atlas_size.width() * m_font_atlas_size.height() * channels);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memset is very c-like. please use c++. use e.g. std::fill

memset(m_font_bitmap, 0, m_font_atlas_size.width() * m_font_atlas_size.height() * channels);

const int outline_bit_count = 4 * (m_font_outline.x - 1) * (m_font_outline.y - 1) + 2 * (m_font_outline.x - 1) + 2 * (m_font_outline.y - 1) + 1;
int* outline_mask = new int[outline_bit_count];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. please use std::vector

const auto font_init = stbtt_InitFont(&m_fontinfo, reinterpret_cast<const uint8_t*>(data.constData()), stbtt_GetFontOffsetForIndex(reinterpret_cast<const uint8_t*>(data.constData()), 0));
assert(font_init);

uint8_t* temp_bitmap = new uint8_t[m_font_atlas_size.width() * m_font_atlas_size.height()];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here (no raw new and delete)

void MapLabel::init(const std::unordered_map<int, const MapLabel::CharData>& character_data, const stbtt_fontinfo* fontinfo)
{
float offset_x = 0;
float offset_y = -font_size / 2.0f + 75.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also converts float -> double -> float

float offset_x = 0;
float offset_y = -font_size / 2.0f + 75.0;

float uv_width_norm = 1.0f / 512.0f;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constexpr


private:
// list of all characters that will be available (will be rendered to the font_atlas)
const std::string all_char_list = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()[]{},;.:-_!\"§$%&/\\=+-*/#'~°^<>|@€´`öÖüÜäÄß";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be a static constexpr auto all_char_list

std::unordered_map<int, const MapLabel::CharData> m_char_data;

stbtt_fontinfo m_fontinfo;
uint8_t* m_font_bitmap;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no naked new and delete. i believe this one we are even leaking :)

you can use the address sanitizer to detect such issues (enable the cmake variable ALP_ENABLE_ADDRESS_SANITIZER, but you have to pull first)

// we are interpolating the far label according to importance
float interpolatedFarLabelDistance = mix(farLabel0, farLabel1, importance);

// apply "soft" distance scaling depending on near/far label values (if option is set as uniform)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the following gives me nicer results:
` // apply "soft" distance scaling depending on near/far label values (if option is set as uniform)
if(label_dist_scaling)
{
float dist_scale = 1.0 - ((dist - nearLabel) / (farLabel1 - nearLabel)) * 0.4f;
scale *= (dist_scale * dist_scale);
}

// importance based scaling
scale *= (importance + 1.5) / 2.5;

`
and with label_dist_scaling set to true

@LucasDworschak
Copy link
Contributor Author

latest commit should address all the requested changes.

@LucasDworschak
Copy link
Contributor Author

closing pull request and reopening from main (as discussed in discord)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants