2017-10-07 19 views
0

私は画像描画をサポートするvulkanエンジンをプログラムしようとしています。しかし、フルスクリーンのクワッドにイメージをレンダリングしようとすると、動作しません。私は1週間以上コードをデバッグしようとしましたが、動作させることができません。私はstb_imageで画像をロードしています。ここではそのためのコードは次のとおりです。vulkanで画像を描画するときにバグがあります

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(); 
} 

VulkanCommandBufferVulkanBufferはのための単なるラッパーですVkBufferおよびVkCommandBuffer

すべての画像のロードが終わると、私はimageViewを作成して、フラグメントシェーダーのディスクリプタをイメージで更新します。しかし、私がプログラムを実行すると、スクリーン上に白い部分しかありません。しかし、フラグメントシェーダに色を1つだけ描画すると、シェーダが動作しています。あなたの質問に答えるためにlink(x64のデバッグ構成でビルド)

+0

検証レイヤーを使用していますか? – krOoze

+1

コードはうまく見えますが、Vulkanでは問題のある箇所を見つけにくいです。バリデーションレイヤーとは何ですか?コンパイル可能なプロジェクトの形式でコードをお持ちですか?このフラグメントだけを分析するよりも、プロジェクト全体をデバッグする方がはるかに簡単です。たぶん、問題は別の場所、たとえば記述子セットの中にあるかもしれません。 – Ekzuzy

+0

あなたはそうかもしれません。質問を編集し、Visual Studioプロジェクトをzipダウンロードとしてリンクします。 – Max

答えて

1

:ここ

は、完全なVisual Studioの2017年のプロジェクトへのリンクです。ディスクリプタセットの問題があります。具体的には、描画する前にコマンドバッファにバインドしません。ディスクリプタセットの割り当てと更新を除いて、ディスクリプタセットをバインドする必要があります。記述子セットを更新すると、特定のリソース(画像、サンプラ、バッファ)が所定の記述子セットに関連付けられます。この方法では、さまざまなリソースを持つ複数のディスクリプタセットを使用できます。しかし、指定された記述子セットを使用するには、描画する前にバインドする必要があります。バインド操作は、使用する記述子セット(したがって、どのリソースを使用して描画するか)を指定するための、vkCmdBindDescriptorSets()関数呼び出しによって行われます。あなたのコードでvkCmdBindDescriptorSets()関数は決して呼び出されません(どこでも使用されていないか、少なくともVisual Studioはそれを見つけられませんでした)。

  1. 間違ったバージョンはバルカンインスタンスの作成時に指定されています

    は、しかし、私はまた、あなたのコードについていくつかのコメントがあります。 1.0.0の代わりに0.0.1のバージョンを指定します。 VulkanのすべてのドライバとSDKのバージョンは古いバージョンのVulkanと互換性がある(パッチのバージョンが低い)ので、パッチのバージョンは現時点では重要ではありません。

  2. 間違った仮定(と警告メッセージが)swapchain作成時に表示されます。表面機能の

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

minImageCountメンバーは、あなたが画像の少なくともその数とswapchainを作成できることを意味します(あなたはあまり要求できません)。しかし、ダブルバッファリングがサポートされていないわけではありません。運転手が3を指定するとどうなりますか? 3つの画像ではダブルバッファリング(またはトリプルバッファリング)で十分ですが、ここでは3つの画像もブレークします。

  1. ブレンドを有効にしてグラフィックスパイプラインを作成します。フラグメントシェーダを赤色の出力(1,0,0,0)だけに設定すると、透明であるため色が見えなくなりました。私はそれを(1、0、0、1)に変更しなければならなかった。もちろん、有効ですが、デバッグの問題を引き起こすことがあります。

  2. あなたの描画コードは奇妙な方法で構成されています。ディスクリプタセットレイアウト、パイプラインレイアウト(ディスクリプタセットを使用)、グラフィックスパイプラインを作成します。次に、パイプラインをバインドしてジオメトリを描画するコマンドバッファをすぐに記録します。パイプラインはすでに(パイプラインレイアウトとフラグメントシェーダで指定されているように)ディスクリプタセットを使用していますが、これまで更新されていないため、これは無効です。数フレーム後にディスクリプタセットを更新します。この時点以降では、図面コードが正しく始まります。将来の潜在的な問題を避けるために、それをリファクタリングする必要があります。

  3. テクスチャ座標が間違っています。しかし、ディスクリプタセットの問題を修正すると、この問題を見つけるのは簡単です。

+0

フィードバックありがとう!私はvulkanにはあまり経験がありませんので、これは本当に私を助けます! – Max

+0

うれしい私は助けることができました。問題がある場合は、私に連絡することを躊躇しないでください。 – Ekzuzy

関連する問題