代码之家  ›  专栏  ›  技术社区  ›  Max

我在用vulkan绘制图像时有一个错误

  •  2
  • Max  · 技术社区  · 7 年前

    我正在尝试编程一个vulkan引擎,它也支持图像绘制。但是,当我尝试将图像渲染到全屏四元屏幕时,它不起作用。我试着调试代码一个多星期了,但我无法让它工作。我正在加载图像 stb_image . 以下是代码:

    errno = 0;
    
    m_imageData = stbi_load(filename.c_str(), &m_width, &m_height, &m_channels, STBI_rgb_alpha);
    if (m_imageData == nullptr) {
        String error = stbi_failure_reason();
        console::printErr("Image loading failed!\nImage: "_d + filename + "\nFailure reason: " + error + "\n" + strerror(errno));
    }
    

    然后我通过 m_imageData 对于此方法:

    m_device = vulkanManager.getDevice().getDevice();
    m_imageSize = width * height * 4;
    
    VkImageCreateInfo imageCreateInfo;
    imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    imageCreateInfo.pNext = nullptr;
    imageCreateInfo.flags = 0;
    imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
    imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
    imageCreateInfo.extent.width = width;
    imageCreateInfo.extent.height = height;
    imageCreateInfo.extent.depth = 1;
    imageCreateInfo.mipLevels = 1;
    imageCreateInfo.arrayLayers = 1;
    imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
    imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
    imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    imageCreateInfo.queueFamilyIndexCount = 0;
    imageCreateInfo.pQueueFamilyIndices = nullptr;
    imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    
    VkResult result = vkCreateImage(m_device, &imageCreateInfo, nullptr, &m_image);
    debug::Assert_Vulkan(result);
    
    void* stagingBufferMemory;
    
    VulkanBuffer stagingBuffer((uint)m_imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
    stagingBuffer.mapMemory(&stagingBufferMemory, 0);
    
    memcpy(stagingBufferMemory, pixels, m_imageSize);
    
    stagingBuffer.unmapMemory();
    
    VkMemoryRequirements imageMemRequirements;
    vkGetImageMemoryRequirements(m_device, m_image, &imageMemRequirements);
    
    VkMemoryAllocateInfo imageMemAllocInfo;
    imageMemAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    imageMemAllocInfo.pNext = nullptr;
    imageMemAllocInfo.allocationSize = imageMemRequirements.size;
    imageMemAllocInfo.memoryTypeIndex = vulkanManager.findMemoryTypeIndex(imageMemRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
    
    result = vkAllocateMemory(m_device, &imageMemAllocInfo, nullptr, &m_imageMemory);
    debug::Assert_Vulkan(result);
    
    vkBindImageMemory(m_device, m_image, m_imageMemory, 0);
    
    VulkanCommandBuffer copyBufferToImage(vulkanManager.getDevice().getGraphicsQueueIndex());
    copyBufferToImage.startCmdBuffer(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
    
    VkBufferImageCopy region;
    region.bufferOffset = 0;
    region.bufferRowLength = 0;
    region.bufferImageHeight = 0;
    region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    region.imageSubresource.mipLevel = 0;
    region.imageSubresource.baseArrayLayer = 0;
    region.imageSubresource.layerCount = 1;
    region.imageOffset = { 0, 0, 0 };
    region.imageExtent = { width, height, 1 };
    
    vkCmdCopyBufferToImage(copyBufferToImage.getCommandBuffer(), stagingBuffer.getBuffer(), m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
    
    copyBufferToImage.endCmdBuffer();
    
    transitionImageLayout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
    
    copyBufferToImage.execute();
    
    transitionImageLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
    stagingBuffer.destroy();
    

    transitionImageLayout是一个辅助函数:

    void VulkanImage::transitionImageLayout(VkImageLayout oldLayout, VkImageLayout newLayout)
    {
        VulkanCommandBuffer cmdBuffer(vulkanManager.getDevice().getGraphicsQueueIndex());
        cmdBuffer.startCmdBuffer(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
    
        VkPipelineStageFlags srcStage, dstStage;
    
        VkImageMemoryBarrier memoryBarrier;
    
        if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
            memoryBarrier.srcAccessMask = 0;
            memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    
            srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
            dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
        }
        else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
            memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
            memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
    
            srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
            dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
        }
    
        memoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
        memoryBarrier.pNext = nullptr;
        memoryBarrier.oldLayout = oldLayout;
        memoryBarrier.newLayout = newLayout;
        memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
        memoryBarrier.image = m_image;
        memoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        memoryBarrier.subresourceRange.baseArrayLayer = 0;
        memoryBarrier.subresourceRange.baseMipLevel = 0;
        memoryBarrier.subresourceRange.layerCount = 1;
        memoryBarrier.subresourceRange.levelCount = 1;
    
        vkCmdPipelineBarrier(
            cmdBuffer.getCommandBuffer(),
            srcStage, dstStage,
            0,
            0, nullptr,
            0, nullptr,
            1, &memoryBarrier
        );
    
        cmdBuffer.endCmdBuffer();
        cmdBuffer.execute();
    }
    

    VulkanCommandBuffer VulkanBuffer 只是一个包装 VkBuffer VkCommandBuffer .

    在所有图像加载之后,我创建了一个imageView,并用图像更新片段着色器中的描述符。但当我执行程序时,屏幕上只有白色。但是当我在片段着色器中只绘制一种颜色时,我的着色器就可以工作。

    以下是完整Visual Studio 2017项目的链接: link (使用x64调试配置构建)

    1 回复  |  直到 7 年前
        1
  •  2
  •   Ekzuzy    7 年前

    vkCmdBindDescriptorSets() 函数调用,通过该调用可以指定应使用哪些描述符集(从而-应使用哪些资源进行绘制)。在您的代码中 vkCmdBindDescriptorSets() 函数从未被调用(它没有在任何地方使用,或者至少Visual Studio没有找到它)。

    但我对您的代码也有一些评论:

    1. Vulkan实例创建期间指定了错误的版本。指定0.0.1版本而不是1.0.0。补丁版本目前并不重要,因为所有Vulkan驱动程序和SDK版本都应该与较旧的Vulkan版本兼容(具有较低的补丁版本)。

    2. 创建交换链期间显示错误假设(和警告消息):

    if (surface.getSurfaceCapabilities().minImageCount != 2) debug::Break("Your graphics device does not support double buffering!");

    最小计数

    1. 您可以在启用混合的情况下创建图形管道。当我将片段着色器设置为仅提供红色输出(1,0,0,0)时,颜色不可见(因为它是透明的)。我不得不将其更改为(1,0,0,1)。当然,它是有效的,但有时可能会导致调试问题。

    2. 纹理坐标错误。但一旦你解决了描述符集的问题,就很容易发现这个问题。