You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Vulkan:每个对象配不同纹理的动态缓冲渲染异常问题

排查Vulkan添加COMBINED_IMAGE_SAMPLER后仅显示纹理背景色的问题

嘿,我碰到过一模一样的坑!在Vulkan里添加VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER后只显示纹理背景色,不用改着色器的话,大概率是描述符配置、资源状态或者绑定匹配出了问题,咱们一步步排查:

  • 检查描述符写入的核心参数
    你贴的代码只写了开头,得确认几个关键字段没写错:

    • dstBinding必须和着色器里的绑定号完全对应,比如着色器里写layout(binding = 2) uniform sampler2D mainTex;,那这里就得设成2,别搞混了绑定顺序
    • descriptorCount如果是单个纹理的话必须设为1,设成0的话等于没写描述符
    • 重点看pImageInfo指向的VkDescriptorImageInfo
      • imageView要确保是正确创建的纹理视图,格式必须和纹理本身、着色器采样格式匹配(比如纹理是VK_FORMAT_R8G8B8A8_UNORM,视图就不能用VK_FORMAT_R8G8B8_UNORM
      • sampler要确认是有效创建的,比如如果需要线性过滤,得把magFilterminFilter设为VK_FILTER_LINEAR,地址模式也要符合你的需求
      • imageLayout必须是VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL!这是最容易踩的坑——如果纹理还停留在VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL或者初始的VK_IMAGE_LAYOUT_UNDEFINED,着色器根本读不到数据,自然只能显示背景色
  • 确认纹理的布局状态转换
    你得在把纹理传给描述符之前,通过管线屏障把它的布局转到着色器可读的状态,示例代码大概是这样:

    VkImageMemoryBarrier barrier{};
    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; // 之前的布局
    barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrier.image = textureImage;
    barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    barrier.subresourceRange.baseMipLevel = 0;
    barrier.subresourceRange.levelCount = 1;
    barrier.subresourceRange.baseArrayLayer = 0;
    barrier.subresourceRange.layerCount = 1;
    barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
    
    vkCmdPipelineBarrier(cmdBuffer,
        VK_PIPELINE_STAGE_TRANSFER_BIT,
        VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
        0,
        0, nullptr,
        0, nullptr,
        1, &barrier);
    

    这里要注意srcStageMaskdstStageMasksrcAccessMaskdstAccessMask的对应关系,不能乱设。

  • 核对描述符集布局与管线布局的一致性

    • 创建描述符集布局时,对应绑定点的descriptorType必须是VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,别写成VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE(后者是单独的图像,需要和采样器分开绑定)
    • 管线布局引用的描述符集布局,必须和你分配描述符集时用的布局完全一致,不然管线会找不到正确的绑定资源
  • 验证纹理数据是否正确上传
    有时候看似上传了数据,其实拷贝过程出了问题:

    • 如果是用主机映射内存上传的,要确认拷贝后调用了vkFlushMappedMemoryRanges,把数据刷到设备内存里
    • 可以用RenderDoc这类工具抓帧,直接查看纹理的内容是不是正确的,排除数据本身的问题

最后给你一个完整的描述符写入示例,你可以对照自己的代码找差异:

// 先准备图像信息
VkDescriptorImageInfo imageInfo{};
imageInfo.imageView = yourTextureImageView;
imageInfo.sampler = yourTextureSampler;
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;

// 填充描述符写入结构
VkWriteDescriptorSet writeSet{};
writeSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeSet.dstSet = yourDescriptorSet;
writeSet.dstBinding = 1; // 对应着色器的绑定号
writeSet.dstArrayElement = 0;
writeSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeSet.descriptorCount = 1;
writeSet.pImageInfo = &imageInfo;

// 更新描述符集
vkUpdateDescriptorSets(yourDevice, 1, &writeSet, 0, nullptr);

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

火山引擎 最新活动