packages feed

keid-render-basic-0.1.3.0: src/Render/ForwardMsaa.hs

module Render.ForwardMsaa
  ( ForwardMsaa(..)
  , allocateMsaa
  , updateMsaa
  , usePass
  ) where

import RIO

import Control.Monad.Trans.Resource qualified as Resource
import Data.Bits ((.|.))
import Data.Vector qualified as Vector
import Vulkan.Core10 qualified as Vk
import Vulkan.Utils.Debug qualified as Debug
import Vulkan.Zero (zero)

import Engine.Types.RefCounted (RefCounted, newRefCounted, releaseRefCounted, resourceTRefCount)
import Engine.Vulkan.Types (HasVulkan(..), HasSwapchain(..), HasRenderPass(..), RenderPass(..), MonadVulkan)
import Render.Pass (usePass)
import Resource.Image (AllocatedImage)
import Resource.Image qualified as Image

-- * Simple MSAA-enabled pass

data ForwardMsaa = ForwardMsaa
  { fmRenderPass   :: Vk.RenderPass
  , fmColor        :: AllocatedImage
  , fmDepth        :: AllocatedImage
  , fmFrameBuffers :: Vector Vk.Framebuffer
  , fmRenderArea   :: Vk.Rect2D
  , fmClear        :: Vector Vk.ClearValue
  , fmRelease      :: RefCounted
  }

instance HasRenderPass ForwardMsaa where
  getRenderPass          = fmRenderPass
  getFramebuffers        = fmFrameBuffers
  getClearValues         = fmClear
  getRenderArea          = fmRenderArea

instance RenderPass ForwardMsaa where
  allocateRenderpass_ = allocateMsaa -- error "Thou shalt allocateMsaa instead"

  updateRenderpass = updateMsaa

  refcountRenderpass = resourceTRefCount . fmRelease

allocateMsaa
  :: ( Resource.MonadResource m
     , MonadVulkan env m
     , HasLogFunc env
     , HasSwapchain swapchain
     )
  => swapchain
  -> m ForwardMsaa
allocateMsaa swapchain = do
  logDebug "Allocating ForwardMsaa resources"
  (_rpKey, renderPass) <- allocateRenderPassMsaa swapchain
  (refcounted, color, depth, framebuffers) <- allocateFramebufferMsaa swapchain renderPass

  pure ForwardMsaa
    { fmRenderPass   = renderPass
    , fmRenderArea   = fullSurface
    , fmClear        = clear
    , fmColor        = color
    , fmDepth        = depth
    , fmFrameBuffers = framebuffers
    , fmRelease      = refcounted
    }
  where
    fullSurface = Vk.Rect2D
      { Vk.offset = zero
      , Vk.extent = getSurfaceExtent swapchain
      }
    clear = Vector.fromList
      [ clearColor
      , Vk.DepthStencil (Vk.ClearDepthStencilValue 1.0 0)
      , clearColor
      ]
    clearColor = Vk.Color zero

updateMsaa
  :: ( Resource.MonadResource m
     , MonadVulkan env m
     , HasLogFunc env
     , HasSwapchain swapchain
     )
  => swapchain
  -> ForwardMsaa
  -> m ForwardMsaa
updateMsaa swapchain old@ForwardMsaa{fmRelease, fmRenderPass} = do
  releaseRefCounted fmRelease
  (refcounted, color, depth, framebuffers) <- allocateFramebufferMsaa swapchain fmRenderPass
  pure old
    { fmColor        = color
    , fmDepth        = depth
    , fmFrameBuffers = framebuffers
    , fmRelease      = refcounted
    , fmRenderArea   = fullSurface
    }
  where
    fullSurface = Vk.Rect2D
      { Vk.offset = zero
      , Vk.extent = getSurfaceExtent swapchain
      }

-- ** Render pass

allocateRenderPassMsaa
  :: ( MonadVulkan env m
     , Resource.MonadResource m
     , HasSwapchain swapchain
     )
  => swapchain
  -> m (Resource.ReleaseKey, Vk.RenderPass)
allocateRenderPassMsaa swapchain = do
  device <- asks getDevice
  let
    format = getSurfaceFormat swapchain
    depthFormat = getDepthFormat swapchain
    msaa = getMultisample swapchain

    attachments =
      [ color format msaa
      , depth depthFormat msaa
      , colorResolve format
      ]

  res@(_key, object) <- Vk.withRenderPass device (createInfo attachments) Nothing Resource.allocate
  Debug.nameObject device object "ForwardMSAA"
  pure res
  where
    createInfo attachments = zero
      { Vk.attachments  = Vector.fromList attachments
      , Vk.subpasses    = Vector.fromList [subpass]
      , Vk.dependencies = Vector.fromList [colorDeps, depthDeps]
      }

    color format msaa = zero
      { Vk.format         = format
      , Vk.samples        = msaa
      , Vk.finalLayout    = Vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
      , Vk.loadOp         = Vk.ATTACHMENT_LOAD_OP_CLEAR
      , Vk.storeOp        = Vk.ATTACHMENT_STORE_OP_DONT_CARE
      , Vk.stencilLoadOp  = Vk.ATTACHMENT_LOAD_OP_DONT_CARE
      , Vk.stencilStoreOp = Vk.ATTACHMENT_STORE_OP_DONT_CARE
      , Vk.initialLayout  = Vk.IMAGE_LAYOUT_UNDEFINED
      }

    depth format msaa = zero
      { Vk.format         = format
      , Vk.samples        = msaa
      , Vk.loadOp         = Vk.ATTACHMENT_LOAD_OP_CLEAR
      , Vk.storeOp        = Vk.ATTACHMENT_STORE_OP_DONT_CARE
      , Vk.stencilLoadOp  = Vk.ATTACHMENT_LOAD_OP_DONT_CARE
      , Vk.stencilStoreOp = Vk.ATTACHMENT_STORE_OP_DONT_CARE
      , Vk.initialLayout  = Vk.IMAGE_LAYOUT_UNDEFINED
      , Vk.finalLayout    = Vk.IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
      }

    colorResolve format = zero
      { Vk.format      = format
      , Vk.samples     = Vk.SAMPLE_COUNT_1_BIT
      , Vk.finalLayout = Vk.IMAGE_LAYOUT_PRESENT_SRC_KHR
      , Vk.loadOp      = Vk.ATTACHMENT_LOAD_OP_DONT_CARE
      }

    subpass = zero
      { Vk.pipelineBindPoint = Vk.PIPELINE_BIND_POINT_GRAPHICS
      , Vk.colorAttachments = Vector.singleton zero
          { Vk.attachment = 0
          , Vk.layout     = Vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
          }
      , Vk.depthStencilAttachment = Just zero
          { Vk.attachment = 1
          , Vk.layout     = Vk.IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
          }
      , Vk.resolveAttachments = Vector.singleton zero
          { Vk.attachment = 2
          , Vk.layout     = Vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
          }
      }

    colorDeps = zero
      { Vk.srcSubpass    = Vk.SUBPASS_EXTERNAL
      , Vk.dstSubpass    = 0
      , Vk.srcStageMask  = colorOut
      , Vk.srcAccessMask = zero
      , Vk.dstStageMask  = colorOut
      , Vk.dstAccessMask = colorRW
      }
      where
        colorOut =
          Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT

        colorRW =
          Vk.ACCESS_COLOR_ATTACHMENT_READ_BIT .|.
          Vk.ACCESS_COLOR_ATTACHMENT_WRITE_BIT

    depthDeps = zero
      { Vk.srcSubpass    = Vk.SUBPASS_EXTERNAL
      , Vk.dstSubpass    = 0
      , Vk.srcStageMask  = fragTests
      , Vk.srcAccessMask = depthW
      , Vk.dstStageMask  = fragTests
      , Vk.dstAccessMask = depthRW
      }
      where
        fragTests =
          Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT .|.
          Vk.PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT

        depthW =
          Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT

        depthRW =
          Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT .|.
          depthW

-- ** Framebuffer

type FramebuffersMsaa =
  ( RefCounted
  , Image.AllocatedImage
  , Image.AllocatedImage
  , Vector Vk.Framebuffer
  )

allocateFramebufferMsaa
  :: ( Resource.MonadResource m
     , MonadVulkan env m
     , HasLogFunc env
     , HasSwapchain swapchain
     )
  => swapchain
  -> Vk.RenderPass
  -> m FramebuffersMsaa
allocateFramebufferMsaa swapchain renderPass = do
  context <- ask
  let extent@Vk.Extent2D{width, height} = getSurfaceExtent swapchain

  (colorKey, color) <- Resource.allocate
    ( Image.create
        context
        (Just $ "ForwardMSAA.color")
        Vk.IMAGE_ASPECT_COLOR_BIT
        extent
        1
        1
        (getMultisample swapchain)
        (getSurfaceFormat swapchain)
        Vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT
    )
    (Image.destroy context)

  (depthKey, depth) <- Resource.allocate
    ( Image.create
        context
        (Just $ "ForwardMSAA.depth")
        Vk.IMAGE_ASPECT_DEPTH_BIT
        extent
        1
        1
        (getMultisample swapchain)
        (getDepthFormat swapchain)
        Vk.IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT
    )
    (Image.destroy context)

  perView <- Vector.iforM (getSwapchainViews swapchain) \ix colorResolve -> do
    let
      attachments = Vector.fromList
        [ Image.aiImageView color
        , Image.aiImageView depth
        , colorResolve
        ]

      fbCI = zero
        { Vk.renderPass  = renderPass
        , Vk.width       = width
        , Vk.height      = height
        , Vk.attachments = attachments
        , Vk.layers      = 1
        }

      device = getDevice context
    res <- Vk.withFramebuffer device fbCI Nothing Resource.allocate
    Debug.nameObject device (snd res) $
      "ForwardMSAA.FB:" <> fromString (show @Int ix)

    pure res

  let (framebufferKeys, framebuffers) = Vector.unzip perView
  releaseDebug <- toIO $ logDebug "Releasing ForwardMsaa resources"
  release <- newRefCounted do
    releaseDebug
    Resource.release colorKey
    Resource.release depthKey
    traverse_ Resource.release framebufferKeys

  pure (release, color, depth, framebuffers)