keid-render-basic-0.1.3.0: src/Render/ShadowMap/RenderPass.hs
{-# LANGUAGE OverloadedLists #-}
module Render.ShadowMap.RenderPass
( ShadowMap(..)
, allocate
, 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.Core11.Promoted_From_VK_KHR_multiview qualified as Khr
import Vulkan.NamedType ((:::))
import Vulkan.Utils.Debug qualified as Debug
import Vulkan.Zero (zero)
import Vulkan.CStruct.Extends (pattern (:&), pattern (::&))
import Engine.Types.RefCounted (RefCounted, newRefCounted, resourceTRefCount)
import Engine.Vulkan.Types (HasVulkan(..), HasSwapchain(..), HasRenderPass(..), RenderPass(..), MonadVulkan)
import Render.Pass (usePass)
import Resource.Image (AllocatedImage)
import Resource.Image qualified as Image
-- * Depth-only pass for shadowmapping pipelines
data ShadowMap = ShadowMap
{ smRenderPass :: Vk.RenderPass
, smDepthImage :: AllocatedImage
, smFrameBuffer :: Vk.Framebuffer
, smRenderArea :: Vk.Rect2D
, smExtent :: Vk.Extent2D
, smLayerCount :: Word32
, smClear :: Vector Vk.ClearValue
, smRelease :: RefCounted
}
instance HasRenderPass ShadowMap where
getRenderPass = smRenderPass
getFramebuffers = Vector.replicate 10 . smFrameBuffer
getClearValues = smClear
getRenderArea = smRenderArea
instance RenderPass ShadowMap where
allocateRenderpass_ = error "Don't allocate ShadowMap via class"
updateRenderpass = error "Don't recreate ShadowMap on resize"
refcountRenderpass = resourceTRefCount . smRelease
allocate
:: ( Resource.MonadResource m
, MonadVulkan env m
, HasLogFunc env
, HasSwapchain context
)
=> context
-> Word32
-> "light count" ::: Word32
-> m ShadowMap
allocate context mapSize layerCount = do
logDebug "Allocating ShadowMap resources"
(_rpKey, renderPass) <- allocateRenderPass context (2 ^ layerCount - 1) 0
(refcounted, depthImage, framebuffer) <- allocateFramebuffer context extent layerCount renderPass
pure ShadowMap
{ smRenderPass = renderPass
, smExtent = extent
, smLayerCount = layerCount
, smRenderArea = fullSurface
, smClear = clear
, smDepthImage = depthImage
, smFrameBuffer = framebuffer
, smRelease = refcounted
}
where
extent = Vk.Extent2D{width=mapSize, height=mapSize}
fullSurface = Vk.Rect2D
{ Vk.offset = zero
, Vk.extent = extent
}
clear = Vector.fromList
[ Vk.DepthStencil (Vk.ClearDepthStencilValue 1.0 0)
]
-- ** Render pass
allocateRenderPass
:: ( MonadVulkan env m
, Resource.MonadResource m
, HasSwapchain swapchain
)
=> swapchain
-> Word32
-> Word32
-> m (Resource.ReleaseKey, Vk.RenderPass)
allocateRenderPass swapchain viewMask correlationMask = do
device <- asks getDevice
res@(_key, object) <- Vk.withRenderPass device createInfo Nothing Resource.allocate
Debug.nameObject device object "ShadowMap.RP"
pure res
where
depthFormat = getDepthFormat swapchain
createInfo =
zero
{ Vk.attachments = Vector.fromList [depth]
, Vk.subpasses = Vector.fromList [subpass]
, Vk.dependencies = Vector.fromList [pre, post]
}
::& Khr.RenderPassMultiviewCreateInfo
{ Khr.viewMasks = [viewMask]
, Khr.viewOffsets = []
, Khr.correlationMasks = [correlationMask]
}
:& ()
depth = zero
{ Vk.format = depthFormat
, Vk.samples = Vk.SAMPLE_COUNT_1_BIT
, Vk.loadOp = Vk.ATTACHMENT_LOAD_OP_CLEAR
, Vk.storeOp = Vk.ATTACHMENT_STORE_OP_STORE
, 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_READ_ONLY_OPTIMAL
}
subpass = zero
{ Vk.pipelineBindPoint = Vk.PIPELINE_BIND_POINT_GRAPHICS
, Vk.depthStencilAttachment = Just zero
{ Vk.attachment = 0
, Vk.layout = Vk.IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
}
}
pre = zero
{ Vk.dependencyFlags = Vk.DEPENDENCY_BY_REGION_BIT
, Vk.srcSubpass = Vk.SUBPASS_EXTERNAL
, Vk.srcStageMask = Vk.PIPELINE_STAGE_FRAGMENT_SHADER_BIT
, Vk.srcAccessMask = Vk.ACCESS_SHADER_READ_BIT
, Vk.dstSubpass = 0
, Vk.dstStageMask = Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
, Vk.dstAccessMask = Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
}
post = zero
{ Vk.dependencyFlags = Vk.DEPENDENCY_BY_REGION_BIT
, Vk.srcSubpass = 0
, Vk.srcStageMask = Vk.PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
, Vk.srcAccessMask = Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
, Vk.dstSubpass = Vk.SUBPASS_EXTERNAL
, Vk.dstStageMask = Vk.PIPELINE_STAGE_FRAGMENT_SHADER_BIT
, Vk.dstAccessMask = Vk.ACCESS_SHADER_READ_BIT
}
-- ** Framebuffer
type Framebuffers =
( RefCounted
, Image.AllocatedImage
, Vk.Framebuffer
)
allocateFramebuffer
:: ( Resource.MonadResource m
, MonadVulkan env m
, HasLogFunc env
, HasSwapchain swapchain
)
=> swapchain
-> Vk.Extent2D
-> Word32
-> Vk.RenderPass
-> m Framebuffers
allocateFramebuffer swapchain extent layerCount renderPass = do
context <- ask
(depthKey, depth) <- Resource.allocate
( Image.create
context
(Just "ShadowMap.depth")
Vk.IMAGE_ASPECT_DEPTH_BIT
extent
1
layerCount
Vk.SAMPLE_COUNT_1_BIT
(getDepthFormat swapchain)
(Vk.IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT .|. Vk.IMAGE_USAGE_SAMPLED_BIT)
)
(Image.destroy context)
let
attachments = Vector.fromList
[ Image.aiImageView depth
]
{- XXX:
If the render pass uses multiview, then layers must be one and each attachment
requires a number of layers that is greater than the maximum bit index set in
the view mask in the subpasses in which it is used.
-}
fbNumLayers = 1
Vk.Extent2D{width, height} = extent
fbCI = zero
{ Vk.renderPass = renderPass
, Vk.width = width
, Vk.height = height
, Vk.attachments = attachments
, Vk.layers = fbNumLayers
}
device = getDevice context
(framebufferKey, framebuffer) <- Vk.withFramebuffer device fbCI Nothing Resource.allocate
Debug.nameObject device framebuffer "ShadowMap.FB"
releaseDebug <- toIO $ logDebug "Releasing ShadowMap resources"
release <- newRefCounted do
releaseDebug
Resource.release depthKey
Resource.release framebufferKey
pure (release, depth, framebuffer)