0

我一直在尝试创建自己的 IMGUI 库,但找不到识别 IMGUI 调用问题的解决方案。

示例代码:

for (std::size_t i = 0; i < options.size(); ++i)
    if (ui.radio_button(selected_option == i, options[i].str))
        selected_option = i;

与 IMGUI 模式一样,UI 创建代码和相关逻辑运行每一帧。每个小部件调用都会立即返回它是否已在前一帧进行过交互。因此,我需要识别帧之间的小部件。这里的“小部件”通常是一个函数调用,或者是父小部件内的“嵌套”函数调用:

{
    auto col = ui.scoped_column();
    ui.widget1(...);
    ui.widget2(...);
    ui.widget3(...);
    {
        auto row = ui.scoped_row();
        ui.widget4(...);
        ui.widget5(...);
        ui.widget6(...);
    }
    ui.widget7(...);
}

对于创建交互式小部件(按钮、单选、悬停、下拉)的每个调用,我都需要一些 ID。ID 被放入一个哈希表中,其中存储了一些与交互相关的信息(悬停、点击、边界等),下一帧将检查该表是否在调用相应的小部件 ID 时发生了某些事情。

因此,我需要一些方法来唯一标识小部件函数调用。

到目前为止我已经尝试过:小部件输入和当前小部件树状态的哈希。亲爱的 ImGui 库使用这种机制。我也试过了,我不得不说:它被设计为在某些时候失败。ID 必须是绝对唯一的,并且在循环/递归中使用少量小部件的 hello-world 实验中,我遇到了 2 种模式,这些模式会为以下哈希函数生成无限数量的冲突:

// https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function
std::uint64_t hash_fnv1a_64(const void* data, std::size_t len) noexcept
{
    std::uint64_t hash = UINT64_C(0xcbf29ce484222325);
    auto ptr = reinterpret_cast<const unsigned char*>(data);
    const auto end = ptr + len;

    while (ptr != end)
        hash ^= (hash ^ *ptr++) * UINT64_C(0x100000001b3);

    return hash;
}

模式是:

hash: 6000921948455098694; lengths: 34, 42, 50
202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 114 101 99 117 114 115 101 46 46 46
202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 114 101 99 117 114 115 101 46 46 46
202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 114 101 99 117 114 115 101 46 46 46

hash: 10160622389312194022; lengths: 35, 43, 51
202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 109 111 114 101 46 46 46
202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 109 111 114 101 46 46 46
202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 211 0 0 0 202 0 0 0 109 111 114 101 46 46 46

所以简而言之,我需要一种保证绝对唯一性的机制。任何重复的 ID(在我的项目中)都会导致未定义的行为(具有重复 ID 的上下文菜单会导致无限递归)。

我尝试过的其他一些想法及其问题:

  • per-function-call increment:这很简单,但是如果下一帧包含不同数量的小部件,ID 将被移动并匹配不同的小部件。一个潜在的解决方案是每帧运行两次 UI 代码(第一次没有交互),但我担心是否有任何看不见的后果/问题
  • 使用对象地址:C++ 保证每个对象都有一个唯一的地址,但是:
    • 放入小部件(例如字符串)的数据地址可以在帧之间更改
    • 某些地址可能会在 1 帧内意外重用(例如ui.widget(str1 + str2)
    • 不保证字符串文字具有唯一地址
  • 使用__LINE__- 不能跨文件工作并且不支持动态小部件
  • 在 UI 对象中注册每个交互式小部件,但随后它使动态小部件复杂化,并且通过将小部件对象保留在框架之间的库用户代码中来破坏 IMGUI 的目的
  • 多种方法的某种组合?

我知道可能没有理想的解决方案,但我对任何支持/反对特定解决方案的论点感兴趣。由于多种原因,哈希不起作用(没有数据长度保证、相同的字符串、冲突、不同对象的相同二进制表示)

4

0 回答 0