packages feed

keid-core-0.1.8.0: src/Engine/Render.hs

{-# LANGUAGE OverloadedLists #-}

module Engine.Render where

import RIO
import Vulkan.Exception (VulkanException(..))

import Control.Monad.Trans.Resource qualified as Resource
import Vulkan.Core10 qualified as Vk
import Vulkan.Core10.CommandBuffer qualified as CommandBuffer
import Vulkan.Core12.Promoted_From_VK_KHR_timeline_semaphore qualified as Vk12
import Vulkan.CStruct.Extends (SomeStruct(..), pattern (:&), pattern (::&))
import Vulkan.Extensions.VK_KHR_swapchain qualified as Khr
import Vulkan.NamedType ((:::))
import Vulkan.Zero (zero)

import Engine.Frame qualified as Frame
import Engine.Types (Frame(..), RecycledResources(..), StageFrameRIO)
import Engine.Types.RefCounted (resourceTRefCount)
import Engine.Vulkan.Swapchain (SwapchainInfo(..), SwapchainResources(..))
import Engine.Vulkan.Types (HasVulkan(..), RenderPass(..), Queues(..))

renderFrame
  :: RenderPass rp
  => (rr -> StageFrameRIO rp p rr st ())
  -> (Vk.CommandBuffer -> rr -> "image index" ::: Word32 -> StageFrameRIO rp p rr st ())
  -> StageFrameRIO rp p rr st ()
renderFrame updateBuffers recordCommandBuffer = do
  Frame{..} <- asks snd
  let stageRecycled = rrData fRecycledResources
  device <- asks getDevice

  let oneSecondKhr = 1e9
  let
    RecycledResources{..}  = fRecycledResources
    SwapchainResources{..} = fSwapchainResources
    SwapchainInfo{..}      = srInfo

  resourceTRefCount srRelease
  resourceTRefCount (fst fStageResources)
  refcountRenderpass fRenderpass

  updateBuffers stageRecycled

  (res, imageIndex) <- Khr.acquireNextImageKHRSafe
    device
    siSwapchain
    oneSecondKhr
    rrImageAvailableSemaphore
    Vk.NULL_HANDLE

  let
    proceed = do
      -- Allocate a command buffer and populate it
      let
        commandBufferAI = zero
          { Vk.commandPool        = qGraphics rrQueues
          , Vk.level              = Vk.COMMAND_BUFFER_LEVEL_PRIMARY
          , Vk.commandBufferCount = 1
          }
      commandBuffer <- Vk.withCommandBuffers device commandBufferAI Resource.allocate >>= \case
        (_key, [one]) ->
          pure one
        _ ->
          throwString "assert: 1 buffer allocated as requested"

      let
        commandBufferBI = zero
          { CommandBuffer.flags = Vk.COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
          }
      Vk.useCommandBuffer commandBuffer commandBufferBI $
        recordCommandBuffer commandBuffer stageRecycled imageIndex

      let
        submitInfo =
          zero
            { Vk.waitSemaphores =
                [ rrImageAvailableSemaphore
                ]
            , Vk.waitDstStageMask =
                [ Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
                ]
            , Vk.commandBuffers =
                [ Vk.commandBufferHandle commandBuffer
                ]
            , Vk.signalSemaphores =
                [ rrRenderFinishedSemaphore
                , fRenderFinishedHostSemaphore
                ]
            }
          ::& zero
            { Vk12.waitSemaphoreValues   = [1]
            , Vk12.signalSemaphoreValues = [1, fIndex]
            }
          :& ()

      -- traceM $ "Submitting frame " <> textDisplay fIndex <> " to image " <> textDisplay imageIndex
      Queues{qGraphics=(_family, graphicsPresentQueue)} <- asks getQueues
      Frame.queueSubmit
        graphicsPresentQueue
        [SomeStruct submitInfo]
        fGPUWork
        fRenderFinishedHostSemaphore
        fIndex

      -- traceM $ "Presenting image " <> textDisplay imageIndex <> " from frame " <> textDisplay fIndex
      presentRes <- Khr.queuePresentKHR graphicsPresentQueue zero
        { Khr.waitSemaphores = [rrRenderFinishedSemaphore]
        , Khr.swapchains     = [siSwapchain]
        , Khr.imageIndices   = [imageIndex]
        }
      case presentRes of
        Vk.SUCCESS ->
          pure ()
        Vk.SUBOPTIMAL_KHR -> do
          logDebug "[present] Swapchain is suboptimal, forcing update."
          throwM $ VulkanException Vk.ERROR_OUT_OF_DATE_KHR
        _ ->
          -- TODO: check for ERROR_OUT_OF_DATE_KHR
          logWarn $ "Presenting wasn't quite successful: " <> displayShow presentRes

  case res of
    Vk.SUCCESS ->
      -- logDebug $ "Acquired next image ID: " <> displayShow imageIndex
      proceed

    Vk.TIMEOUT ->
      logDebug "Timed out (1s) trying to acquire next image"

    Vk.ERROR_OUT_OF_DATE_KHR -> do
      {- XXX:
        Throwing an exception for 'Engine.Vulkan.Swapchain.threwSwapchainError' to catch.
        See also: 'Engine.Run.step'.
      -}
      logDebug "[acquire] Swapchain out of date"
      throwM $ VulkanException res

    Vk.SUBOPTIMAL_KHR -> do
      {- XXX:
        Converting 'Vk.SUBOPTIMAL_KHR' error to OOD to trigger swapchain update.
      -}
      logDebug "[acquire] Swapchain is suboptimal, forcing update."
      throwM $ VulkanException Vk.ERROR_OUT_OF_DATE_KHR

    _ -> do
      logError $
        "Unexpected Result from acquireNextImageKHR: " <>
        fromString (show res)
      throwM $ VulkanException res