You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SpaiR/imgui-java中基于Vulkan渲染图像?

问题:imgui-java中Vulkan纹理渲染的类型不匹配问题

我基于LWJGL的Vulkan教程实现了Vulkan Java应用中的纹理创建,并且编写了与ImGui C++库ImGui_ImplVulkan_AddTexture逻辑完全一致的Java代码来生成DescriptorSet:

public TextureDescriptorSet(DescriptorPool descriptorPool, DescriptorSetLayout descriptorSetLayout,
                            Texture texture, TextureSampler textureSampler, int binding) {
    try (MemoryStack stack = MemoryStack.stackPush()) {
        Device device = descriptorPool.getDevice();
        LongBuffer pDescriptorSetLayout = stack.mallocLong(1);
        pDescriptorSetLayout.put(0, descriptorSetLayout.getVkDescriptorLayout());
        VkDescriptorSetAllocateInfo allocInfo = VkDescriptorSetAllocateInfo.calloc(stack)
                .sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO)
                .descriptorPool(descriptorPool.getVkDescriptorPool())
                .pSetLayouts(pDescriptorSetLayout);
        allocInfo.descriptorSetCount();

        LongBuffer pDescriptorSet = stack.mallocLong(1);
        vkCheck(vkAllocateDescriptorSets(device.getVkDevice(), allocInfo, pDescriptorSet),
                "Failed to create descriptor set");

        vkDescriptorSet = pDescriptorSet.get(0);
        VkDescriptorImageInfo.Buffer imageInfo = VkDescriptorImageInfo.calloc(1, stack)
                .imageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
                .imageView(texture.getImageView().getVkImageView())
                .sampler(textureSampler.getVkSampler());
        VkWriteDescriptorSet.Buffer descrBuffer = VkWriteDescriptorSet.calloc(1, stack);
        descrBuffer.get(0)
                .sType(VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET)
                .dstSet(vkDescriptorSet)
                .dstBinding(binding)
                .descriptorType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
                .descriptorCount(1)
                .pImageInfo(imageInfo);

        vkUpdateDescriptorSets(device.getVkDevice(), descrBuffer, null);
    }
}

上述代码对应ImGui C++中的实现:

ImTextureID ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout){
VkResult err;

ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo;
VkDescriptorSet descriptor_set;
{
    VkDescriptorSetAllocateInfo alloc_info = {};
    alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
    alloc_info.descriptorPool = v->DescriptorPool;
    alloc_info.descriptorSetCount = 1;
    alloc_info.pSetLayouts = &g_DescriptorSetLayout;
    err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set);
    check_vk_result(err);
}

{
    VkDescriptorImageInfo desc_image[1] = {};
    desc_image[0].sampler = sampler;
    desc_image[0].imageView = image_view;
    desc_image[0].imageLayout = image_layout;
    VkWriteDescriptorSet write_desc[1] = {};
    write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    write_desc[0].dstSet = descriptor_set;
    write_desc[0].descriptorCount = 1;
    write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    write_desc[0].pImageInfo = desc_image;
    vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, NULL);
}

return (ImTextureID)descriptor_set;
}

但imgui-java中的ImGui.image方法接收的textureID为int类型:

public static native void image(int textureID, float sizeX, float sizeY); /*
    ImGui::Image((ImTextureID)(intptr_t)textureID, ImVec2(sizeX, sizeY));
*/

而Vulkan的DescriptorSet是long类型,强制转换为int后数值溢出,导致调用ImGui.imageButton时渲染出默认图像而非自定义纹理:

long img = guiRenderActivity.getTextureDescriptorSet().getVkDescriptorSet();
ImGui.imageButton((int) img, 500, 200);

解决方案

1. 修正imgui-java的原生方法绑定

底层C++代码是将intptr_t转换为ImTextureID,而Java中long对应64位系统的intptr_t。可以修改imgui-java的原生方法定义,将参数类型改为long:

public static native void image(long textureID, float sizeX, float sizeY); /*
    ImGui::Image((ImTextureID)(intptr_t)textureID, ImVec2(sizeX, sizeY));
*/

重新编译imgui-java的绑定后,直接传入long类型的DescriptorSet即可,无需强制转换,避免溢出问题。

2. 建立纹理ID映射表

如果无法修改imgui-java源码,可以创建一个全局的映射表,将long类型的DescriptorSet映射为唯一的intID:

private static final Map<Long, Integer> TEXTURE_ID_MAP = new ConcurrentHashMap<>();
private static int nextTextureId = 1; // 避免0值(默认纹理)

public static int registerTexture(long descriptorSet) {
    return TEXTURE_ID_MAP.computeIfAbsent(descriptorSet, k -> nextTextureId++);
}

同时需要修改ImGui的Vulkan渲染后端,在绘制回调中根据intID查找对应的longDescriptorSet,替换到渲染命令中。这种方式需要对ImGui的Vulkan实现有一定修改能力。

3. 选择适配性更好的ImGui替代绑定

  • LibGDX ImGui:基于LibGDX框架的ImGui绑定,对Vulkan的Java类型适配更完善,原生支持64位指针类型传递,避免类型溢出问题。
  • Kotlin/Native ImGui:如果项目允许使用Kotlin,该绑定对原生类型的处理更灵活,直接支持将Vulkan对象作为纹理ID传递。

内容的提问来源于stack exchange,提问作者Blogger 2015

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:05:56