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

Skybox渲染的VkRenderPass加载操作问题

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

    在Vulkan的RenderPass中,我似乎还有另一个问题。

    在绘制场景时,我首先提交一个commandbuffer,以使用立方体贴图上的大气散射渲染天空,然后使用该立方体贴图绘制天空和太阳。

    绘制skybox并将其存储到立方体贴图中进行采样时使用的renderpass:

      m_pFrameBuffer = rhi->CreateFrameBuffer();
      VkImageView attachment = m_RenderTexture->View();
    
      VkAttachmentDescription attachDesc = CreateAttachmentDescription(
        m_RenderTexture->Format(),
        VK_IMAGE_LAYOUT_UNDEFINED,
        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
        VK_ATTACHMENT_LOAD_OP_CLEAR,
        VK_ATTACHMENT_STORE_OP_STORE,
        VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        VK_ATTACHMENT_STORE_OP_DONT_CARE,
        m_RenderTexture->Samples()
      );
    
      VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
    
      std::array<VkSubpassDependency, 2> dependencies;
      dependencies[0] = CreateSubPassDependency(
        VK_SUBPASS_EXTERNAL,
        VK_ACCESS_MEMORY_READ_BIT,
        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        0,
        VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        VK_DEPENDENCY_BY_REGION_BIT
      );
    
      dependencies[1] = CreateSubPassDependency(
        0,
        VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        VK_SUBPASS_EXTERNAL,
        VK_ACCESS_MEMORY_READ_BIT,
        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        VK_DEPENDENCY_BY_REGION_BIT
      );
    
      VkSubpassDescription subpassDesc = { };
      subpassDesc.colorAttachmentCount = 1;
      subpassDesc.pColorAttachments = &colorRef;
      subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
    
      VkRenderPassCreateInfo renderpassCi = { };
      renderpassCi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
      renderpassCi.attachmentCount = 1;
      renderpassCi.pAttachments = &attachDesc;
      renderpassCi.dependencyCount = static_cast<u32>(dependencies.size());
      renderpassCi.pDependencies = dependencies.data();
      renderpassCi.subpassCount = 1;
      renderpassCi.pSubpasses = &subpassDesc;
    
      VkFramebufferCreateInfo framebufferCi = { };
      framebufferCi.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
      framebufferCi.height = kTextureSize;
      framebufferCi.width = kTextureSize;
      framebufferCi.attachmentCount = 1;
      framebufferCi.layers = 1;
      framebufferCi.pAttachments = &attachment;
    
      m_pFrameBuffer->Finalize(framebufferCi, renderpassCi);
    

    渲染skybox并将其存储到立方体贴图中后,我使用以下renderpass在渲染场景中对天空进行采样。此过程使用VK\u LOAD\u OP\u LOAD,以便在将skybox绘制到渲染场景上时不清除渲染场景:

      // Create a renderpass for the pbr overlay.
      Texture* pbrColor = gResources().GetRenderTexture(PBRColorAttachStr);
      Texture* pbrNormal = gResources().GetRenderTexture(PBRNormalAttachStr);
      Texture* pbrPosition = gResources().GetRenderTexture(PBRPositionAttachStr);
      Texture* pbrRoughMetal = gResources().GetRenderTexture(PBRRoughMetalAttachStr);
      Texture* pbrDepth = gResources().GetRenderTexture(PBRDepthAttachStr);
      Texture* RTBright = gResources().GetRenderTexture(RenderTargetBrightStr);
    
      std::array<VkAttachmentDescription, 6> attachmentDescriptions;
      VkSubpassDependency dependenciesNative[2];
    
      attachmentDescriptions[0] = CreateAttachmentDescription(
        pbrColor->Format(),
        VK_IMAGE_LAYOUT_UNDEFINED,
        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
        VK_ATTACHMENT_LOAD_OP_LOAD,
        VK_ATTACHMENT_STORE_OP_STORE,
        VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        VK_ATTACHMENT_STORE_OP_DONT_CARE,
        pbrColor->Samples()
      );
    
      attachmentDescriptions[1] = CreateAttachmentDescription(
        pbrNormal->Format(),
        VK_IMAGE_LAYOUT_UNDEFINED,
        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
        VK_ATTACHMENT_LOAD_OP_LOAD,
        VK_ATTACHMENT_STORE_OP_STORE,
        VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        VK_ATTACHMENT_STORE_OP_DONT_CARE,
        pbrNormal->Samples()
      );
    
      attachmentDescriptions[2] = CreateAttachmentDescription(
        RTBright->Format(),
        VK_IMAGE_LAYOUT_UNDEFINED,
        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
        VK_ATTACHMENT_LOAD_OP_LOAD,
        VK_ATTACHMENT_STORE_OP_STORE,
        VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        VK_ATTACHMENT_STORE_OP_DONT_CARE,
        RTBright->Samples()
      );
    
      attachmentDescriptions[3] = CreateAttachmentDescription(
        pbrPosition->Format(),
        VK_IMAGE_LAYOUT_UNDEFINED,
        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
        VK_ATTACHMENT_LOAD_OP_LOAD,
        VK_ATTACHMENT_STORE_OP_STORE,
        VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        VK_ATTACHMENT_STORE_OP_DONT_CARE,
        pbrPosition->Samples()
      );
    
      attachmentDescriptions[4] = CreateAttachmentDescription(
        pbrRoughMetal->Format(),
        VK_IMAGE_LAYOUT_UNDEFINED,
        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
        VK_ATTACHMENT_LOAD_OP_LOAD,
        VK_ATTACHMENT_STORE_OP_STORE,
        VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        VK_ATTACHMENT_STORE_OP_DONT_CARE,
        pbrRoughMetal->Samples()
      );
    
      attachmentDescriptions[5] = CreateAttachmentDescription(
        pbrDepth->Format(),
        VK_IMAGE_LAYOUT_UNDEFINED,
        VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
        VK_ATTACHMENT_LOAD_OP_LOAD,
        VK_ATTACHMENT_STORE_OP_STORE,
        VK_ATTACHMENT_LOAD_OP_DONT_CARE,
        VK_ATTACHMENT_STORE_OP_DONT_CARE,
        pbrDepth->Samples()
      );
    
      dependenciesNative[0] = CreateSubPassDependency(
        VK_SUBPASS_EXTERNAL,
        VK_ACCESS_MEMORY_READ_BIT,
        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        0,
        VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        VK_DEPENDENCY_BY_REGION_BIT
      );
    
      dependenciesNative[1] = CreateSubPassDependency(
        0,
        VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        VK_SUBPASS_EXTERNAL,
        VK_ACCESS_MEMORY_READ_BIT,
        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        VK_DEPENDENCY_BY_REGION_BIT
      );
    
      std::array<VkAttachmentReference, 5> attachmentColors;
      VkAttachmentReference attachmentDepthRef = { static_cast<u32>(attachmentColors.size()), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
      attachmentColors[0].attachment = 0;
      attachmentColors[0].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    
      attachmentColors[1].attachment = 1;
      attachmentColors[1].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    
      attachmentColors[2].attachment = 2;
      attachmentColors[2].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    
      attachmentColors[3].attachment = 3;
      attachmentColors[3].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    
      attachmentColors[4].attachment = 4;
      attachmentColors[4].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    
      VkSubpassDescription subpass = {};
      subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
      subpass.colorAttachmentCount = static_cast<u32>(attachmentColors.size());
      subpass.pColorAttachments = attachmentColors.data();
      subpass.pDepthStencilAttachment = &attachmentDepthRef;
    
      VkRenderPassCreateInfo renderpassCI = CreateRenderPassInfo(
        static_cast<u32>(attachmentDescriptions.size()),
        attachmentDescriptions.data(),
        2,
        dependenciesNative,
        1,
        &subpass
      );
    
      VkResult result = 
        vkCreateRenderPass(rhi->LogicDevice()->Native(), &renderpassCI, nullptr, &m_SkyboxRenderPass);
    

    这是用于在场景中渲染天空的命令缓冲区。我在渲染场景后提交此commandbuffer,以利用早期z拒绝:

      if (m_pSkyboxCmdBuffer) {
        m_pRhi->DeviceWaitIdle();
        m_pSkyboxCmdBuffer->Reset(VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
      }
    
      VkCommandBufferBeginInfo beginInfo = { };
      beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    
      CommandBuffer* buf = m_pSkyboxCmdBuffer;
      FrameBuffer* skyFrameBuffer = gResources().GetFrameBuffer(PBRFrameBufferStr);
      GraphicsPipeline* skyPipeline = gResources().GetGraphicsPipeline(SkyboxPipelineStr);
      DescriptorSet* global = m_pGlobal->Set();
      DescriptorSet* skybox = gResources().GetDescriptorSet(SkyboxDescriptorSetStr);
    
      VkDescriptorSet descriptorSets[] = {
        global->Handle(),
        skybox->Handle()
      };  
    
      buf->Begin(beginInfo);
        std::array<VkClearValue, 6> clearValues;
        clearValues[0].color = { 0.0f, 0.0f, 0.0f, 1.0f };
        clearValues[1].color = { 0.0f, 0.0f, 0.0f, 1.0f };
        clearValues[2].color = { 0.0f, 0.0f, 0.0f, 1.0f };
        clearValues[3].color = { 0.0f, 0.0f, 0.0f, 1.0f };
        clearValues[4].color = { 0.0f, 0.0f, 0.0f, 1.0f };
        clearValues[5].depthStencil = { 1.0f, 0 };
    
        VkViewport viewport = {};
        viewport.height = (r32)m_pWindow->Height();
        viewport.width = (r32)m_pWindow->Width();
        viewport.minDepth = 0.0f;
        viewport.maxDepth = 1.0f;
        viewport.y = 0.0f;
        viewport.x = 0.0f;
    
        VkRenderPassBeginInfo renderBegin = { };
        renderBegin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
        renderBegin.framebuffer = skyFrameBuffer->Handle();
        renderBegin.renderPass = m_pSky->GetSkyboxRenderPass();
        renderBegin.clearValueCount = static_cast<u32>(clearValues.size());
        renderBegin.pClearValues = clearValues.data();
        renderBegin.renderArea.offset = { 0, 0 };
        renderBegin.renderArea.extent = m_pRhi->SwapchainObject()->SwapchainExtent();
    
        // Start the renderpass.
        buf->BeginRenderPass(renderBegin, VK_SUBPASS_CONTENTS_INLINE);
          buf->SetViewPorts(0, 1, &viewport);
          buf->BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, skyPipeline->Pipeline());
          buf->BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, skyPipeline->Layout(), 0, 2, descriptorSets, 0, nullptr);
          VertexBuffer* vertexbuffer = m_pSky->GetSkyboxVertexBuffer();
          IndexBuffer* idxBuffer = m_pSky->GetSkyboxIndexBuffer();
    
          VkDeviceSize offsets[] =  { 0 };
          VkBuffer vert = vertexbuffer->Handle()->NativeBuffer();
          VkBuffer ind = idxBuffer->Handle()->NativeBuffer();
          buf->BindVertexBuffers(0 , 1, &vert, offsets);  
          buf->BindIndexBuffer(ind, 0, VK_INDEX_TYPE_UINT32);
          buf->DrawIndexed(idxBuffer->IndexCount(), 1, 0, 0, 0);
        buf->EndRenderPass();
      buf->End();
    

    最后,我在渲染函数中提交它:

      // TODO(): Need to clean this up.
      VkCommandBuffer offscreenCmd = m_Offscreen._CmdBuffers[m_Offscreen._CurrCmdBufferIndex]->Handle();
      VkCommandBuffer skyBuffers[] = { m_Offscreen._CmdBuffers[m_Offscreen._CurrCmdBufferIndex]->Handle(), m_pSky->CmdBuffer()->Handle() };
      VkSemaphore skyWaits[] = { m_Offscreen._Semaphore->Handle(), m_pSky->SignalSemaphore()->Handle() };
      VkSemaphore waitSemas[] = { m_pRhi->SwapchainObject()->ImageAvailableSemaphore() };
      VkSemaphore signalSemas[] = { m_Offscreen._Semaphore->Handle() };
      VkSemaphore shadowSignal[] = { m_Offscreen._ShadowSema->Handle() };
      VkPipelineStageFlags waitFlags[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT };
    
      VkSubmitInfo offscreenSI = {};
      offscreenSI.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
      offscreenSI.pCommandBuffers = &offscreenCmd;
      offscreenSI.commandBufferCount = 1;
      offscreenSI.signalSemaphoreCount = 1;
      offscreenSI.pSignalSemaphores = signalSemas;
      offscreenSI.waitSemaphoreCount = 1;
      offscreenSI.pWaitSemaphores = waitSemas;
      offscreenSI.pWaitDstStageMask = waitFlags;
    
      VkSubmitInfo skyboxSI = offscreenSI;
      VkSemaphore skyboxWaits[] = { m_Offscreen._Semaphore->Handle() };
      VkSemaphore skyboxSignal[] = { m_SkyboxFinished->Handle() };
      VkCommandBuffer skyboxCmd = m_pSkyboxCmdBuffer->Handle();
      skyboxSI.commandBufferCount = 1;
      skyboxSI.pCommandBuffers = &skyboxCmd;
      skyboxSI.pSignalSemaphores = skyboxSignal;
      skyboxSI.pWaitSemaphores = skyboxWaits;
    
      VkSubmitInfo hdrSI = offscreenSI;
      VkSemaphore hdrWaits[] = { m_SkyboxFinished->Handle() };
      VkSemaphore hdrSignal[] = { m_HDR._Semaphore->Handle() };
      VkCommandBuffer hdrCmd = m_HDR._CmdBuffers[m_HDR._CurrCmdBufferIndex]->Handle();
      hdrSI.pCommandBuffers = &hdrCmd;
      hdrSI.pSignalSemaphores = hdrSignal;
      hdrSI.pWaitSemaphores = hdrWaits;
    
      VkSemaphore waitSemaphores = m_HDR._Semaphore->Handle();
      if (!m_HDR._Enabled) waitSemaphores = m_Offscreen._Semaphore->Handle();
    
      // Update materials before rendering the frame.
      UpdateMaterials();
    
      // begin frame. This is where we start our render process per frame.
      BeginFrame();
        while (m_Offscreen._CmdBuffers[m_HDR._CurrCmdBufferIndex]->Recording() || !m_pRhi->CmdBuffersComplete()) {}
    
        // Render shadow map here. Primary shadow map is our concern.
        if (m_pLights->PrimaryShadowEnabled()) {
          VkCommandBuffer shadowbuf[] = { m_Offscreen._ShadowCmdBuffers[m_Offscreen._CurrCmdBufferIndex]->Handle() };
    
          VkSubmitInfo shadowSubmit = { };
          shadowSubmit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
          shadowSubmit.pCommandBuffers = shadowbuf;
          shadowSubmit.commandBufferCount = 1;
          shadowSubmit.signalSemaphoreCount = 1;
          shadowSubmit.waitSemaphoreCount = 1;
          shadowSubmit.pWaitSemaphores = waitSemas;
          shadowSubmit.pSignalSemaphores = shadowSignal;
          shadowSubmit.pWaitDstStageMask = waitFlags;
          // Submit shadow rendering.
          m_pRhi->GraphicsSubmit(shadowSubmit);
    
          offscreenSI.pWaitSemaphores = shadowSignal;
        }
    
        // Check if sky needs to update it's cubemap.
        if (m_pSky->NeedsRendering()) {
          skyboxSI.waitSemaphoreCount = 2;
          skyboxSI.pWaitSemaphores = skyWaits;
          offscreenSI.commandBufferCount = 2;
          offscreenSI.signalSemaphoreCount = 2;
          offscreenSI.pSignalSemaphores = skyWaits;
          offscreenSI.pCommandBuffers = skyBuffers;
          m_pSky->MarkClean();
        }
    
        // Offscreen PBR Forward Rendering Pass.
        m_pRhi->GraphicsSubmit(offscreenSI);
    
        // Render Sky onto our render textures.
        m_pRhi->GraphicsSubmit(skyboxSI);
    
        // High Dynamic Range and Gamma Pass.
        if (m_HDR._Enabled) m_pRhi->GraphicsSubmit(hdrSI);
    
        // Before calling this cmd buffer, we want to submit our offscreen buffer first, then
        // sent our signal to our swapchain cmd buffers.
    
        // TODO(): We want to hold off on signalling GraphicsFinished Semaphore, and instead 
        // have it signal the SignalUI semaphore instead. UI Overlay will be the one to use
        // GraphicsFinished Semaphore to signal end of frame rendering.
        VkSemaphore signal = m_pRhi->GraphicsFinishedSemaphore();
        VkSemaphore uiSig = m_pUI->Signal()->Handle();
        m_pRhi->SubmitCurrSwapchainCmdBuffer(1, &waitSemaphores, 1, &signal);
    
        // Render the Overlay.
        RenderOverlay();
    
      EndFrame();
    

    在Nvidia GTX 870M上,结果似乎与预期一样, Nvidia result

    然而,使用Intel HD Graphics 620,我得到了这个屏幕截图,很遗憾,我无法在这里显示,因为它太大了: https://github.com/CheezBoiger/Recluse-Game/blob/master/Regression/Shaders/ForwardPass.png

    似乎之前帧中的场景未清除到颜色附件上,就好像它渲染到一个单独的曲面上并使用该曲面,但应该在渲染开始时清除每个帧。。。

    删除VK\u LOAD\u OP\u LOAD并替换为VK\u LOAD\u OP\u CLEAR,情况会清除,但是,仅渲染skybox。。。我想知道我的渲染过程是否没有执行需要在英特尔硬件上执行的操作,或者我是否打算将skybox绘制到渲染场景上,但完全错误?

    非常感谢您的帮助。

    *更新* 问题已修复,下面由@Ekzuzy解决。

    修复后英特尔硬件上的最终映像: enter image description here

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

    始终为所有渲染过程中的初始布局和所有附件提供未定义的布局。从未定义布局到任何其他布局的布局转换不能保证图像内容得到保留。因此,如果使用LOAD op的LOAD值创建渲染过程,则需要在渲染过程开始之前提供给定图像的实际布局。这也适用于其他布局转换(通过内存屏障)。

    至于清除,一些图像应该在帧或渲染过程开始时清除。因此,对于它们,可以将未定义作为初始布局,但应将load op更改为clear。

    至于为什么这对Nvidia有效而对Intel无效,布局转换对Nvidia的硬件没有任何影响,但它们对Intel的平台(以及AMD的平台)很重要。因此,跳过(或设置不正确)布局转换,即使它违反了规范,也应该在Nvidia上工作。但不要因为它有效就这么做。这种做法是无效的。而未来的平台,即使来自同一家供应商,也可能表现不同。