packages feed

keid-render-basic-0.1.8.0: src/Render/Pass/Compose.hs

{-# LANGUAGE OverloadedLists #-}

module Render.Pass.Compose
  ( Compose(..)
  , allocate
  , update
  , 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, releaseRefCounted, resourceTRefCount)
import Engine.Types.RefCounted qualified as RefCounted
import Engine.Vulkan.Types (HasSwapchain(..), HasRenderPass(..), MonadVulkan, RenderPass(..), getDevice)
import Render.Pass (usePass)
import Resource.Region qualified as Region

-- | Composition/postprocessing/presentation pass
--
-- Can be used to transfer images from "Render.Pass.Offscreen.Offscreen" passes and tonemapping.
--
-- Color attachments are derived from swapchain.
-- The pass optmized for image transfer: it has no depth attachment and does not clear.
-- Use image blitting that convers the whole area or a fullscreen shader.
data Compose = Compose
  { renderPass   :: Vk.RenderPass
  , frameBuffers :: Vector Vk.Framebuffer
  , renderArea   :: Vk.Rect2D
  , clear        :: Vector Vk.ClearValue
  , release      :: RefCounted
  }

instance HasRenderPass Compose where
  getRenderPass   = renderPass
  getFramebuffers = frameBuffers
  getClearValues  = clear
  getRenderArea   = renderArea

instance RenderPass Compose where
  updateRenderpass = update
  refcountRenderpass = resourceTRefCount . release

allocate
  :: ( Resource.MonadResource m
     , MonadVulkan env m
     , HasLogFunc env
     , HasSwapchain swapchain
     )
  => swapchain
  -> m Compose
allocate swapchain = do
  renderPass <- allocateRenderPass format
  (refcounted, framebuffers) <- RefCounted.wrapped $ Region.run do
    Region.logDebug
      "Allocating Compose resources"
      "Releasing Compose resources"
    allocateFramebufferSwapchain swapchain renderPass

  pure Compose
    { renderPass   = renderPass
    , renderArea   = fullSurface
    , clear        = mempty
    , frameBuffers = framebuffers
    , release      = refcounted
    }
  where
    format = getSurfaceFormat swapchain

    fullSurface = Vk.Rect2D
      { Vk.offset = zero
      , Vk.extent = getSurfaceExtent swapchain
      }

update
  :: ( Resource.MonadResource m
     , MonadVulkan env m
     , HasSwapchain swapchain
     )
  => swapchain
  -> Compose
  -> m Compose
update swapchain old@Compose{release, renderPass} = do
  releaseRefCounted release
  (refcounted, framebuffers) <- RefCounted.wrapped $ Region.run do
    allocateFramebufferSwapchain swapchain renderPass
  pure old
    { frameBuffers = framebuffers
    , release      = refcounted
    , renderArea   = fullSurface
    }
  where
    fullSurface = Vk.Rect2D
      { Vk.offset = zero
      , Vk.extent = getSurfaceExtent swapchain
      }

-- ** Render pass

allocateRenderPass
  :: ( MonadVulkan env m
     , Resource.MonadResource m
     )
  => Vk.Format
  -> m Vk.RenderPass
allocateRenderPass format = do
  device <- asks getDevice
  (_key, rp) <- Vk.withRenderPass device createInfo Nothing Resource.allocate
  Debug.nameObject device rp "Compose"
  pure rp
  where
    createInfo = zero
      { Vk.attachments  = [color]
      , Vk.subpasses    = [subpass]
      , Vk.dependencies = [colorDeps]
      }

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

    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
          }
      }

    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

-- ** Framebuffer

allocateFramebufferSwapchain
  :: ( Resource.MonadResource m
     , MonadVulkan env m
     , HasSwapchain swapchain
     )
  => swapchain
  -> Vk.RenderPass
  -> m (Vector Vk.Framebuffer)
allocateFramebufferSwapchain swapchain renderPass =
  Vector.iforM (getSwapchainViews swapchain) \ix colorView -> do
    let
      fbCI = zero
        { Vk.renderPass  = renderPass
        , Vk.width       = width
        , Vk.height      = height
        , Vk.attachments = [colorView]
        , Vk.layers      = 1
        }

    device <- asks getDevice
    (_key, fb) <- Vk.withFramebuffer device fbCI Nothing Resource.allocate
    Debug.nameObject device fb $
      "Compose.FB:" <> fromString (show @Int ix)
    pure fb
  where
    Vk.Extent2D{width, height} = getSurfaceExtent swapchain