keid-core-0.1.3.0: src/Resource/Image.hs
module Resource.Image
( AllocatedImage(..)
, create
, destroy
, subresource
, transitionLayout
, copyBufferToImage
) where
import RIO
import Data.Bits (shiftR, (.|.))
import RIO.Vector qualified as Vector
import Vulkan.Core10 qualified as Vk
import Vulkan.CStruct.Extends (SomeStruct(..))
import Vulkan.NamedType ((:::))
import Vulkan.Utils.Debug qualified as Debug
import Vulkan.Zero (zero)
import VulkanMemoryAllocator qualified as VMA
import Engine.Vulkan.Types (HasVulkan(..), Queues(..))
import Resource.CommandBuffer (oneshot_)
data AllocatedImage = AllocatedImage
{ aiAllocation :: VMA.Allocation
, aiFormat :: Vk.Format
, aiImage :: Vk.Image
, aiImageView :: Vk.ImageView
, aiImageRange :: Vk.ImageSubresourceRange
}
deriving (Show)
create
:: ( MonadIO io
, HasVulkan ctx
)
=> ctx
-> Maybe Text
-> Vk.ImageAspectFlags
-> Vk.Extent2D
-> "mip levels" ::: Word32
-> "stored layers" ::: Word32
-> Vk.SampleCountFlagBits
-> Vk.Format
-> Vk.ImageUsageFlags
-> io AllocatedImage
create context mlabel aspect Vk.Extent2D{width, height} mipLevels numLayers samples format usage = do
let
device = getDevice context
allocator = getAllocator context
(image, allocation, _info) <- VMA.createImage
allocator
imageCI
imageAllocationCI
for_ mlabel \label ->
Debug.nameObject device image $ encodeUtf8 label <> ".label"
imageView <- Vk.createImageView
device
(imageViewCI image)
Nothing
for_ mlabel \label ->
Debug.nameObject device image $ encodeUtf8 label <> ".view"
pure AllocatedImage
{ aiAllocation = allocation
, aiFormat = format
, aiImage = image
, aiImageView = imageView
, aiImageRange = subr
}
where
imageCI = zero
{ Vk.imageType = Vk.IMAGE_TYPE_2D
, Vk.flags = createFlags
, Vk.format = format
, Vk.extent = Vk.Extent3D width height 1
, Vk.mipLevels = mipLevels
, Vk.arrayLayers = numLayers
, Vk.tiling = Vk.IMAGE_TILING_OPTIMAL
, Vk.initialLayout = Vk.IMAGE_LAYOUT_UNDEFINED
, Vk.usage = usage
, Vk.sharingMode = Vk.SHARING_MODE_EXCLUSIVE
, Vk.samples = samples
}
imageAllocationCI = zero
{ VMA.usage = VMA.MEMORY_USAGE_GPU_ONLY
, VMA.requiredFlags = Vk.MEMORY_PROPERTY_DEVICE_LOCAL_BIT
}
imageViewCI image = zero
{ Vk.image = image
, Vk.viewType = viewType
, Vk.format = format
, Vk.components = zero
, Vk.subresourceRange = subr
}
(createFlags, viewType) =
case numLayers of
1 ->
(zero, Vk.IMAGE_VIEW_TYPE_2D)
6 ->
(Vk.IMAGE_CREATE_CUBE_COMPATIBLE_BIT, Vk.IMAGE_VIEW_TYPE_CUBE)
_ ->
(zero, Vk.IMAGE_VIEW_TYPE_2D_ARRAY)
subr = subresource aspect mipLevels numLayers
destroy
:: ( MonadIO io
, HasVulkan context
)
=> context
-> AllocatedImage
-> io ()
destroy context AllocatedImage{..} = do
-- traceM "destroyAllocatedImage"
Vk.destroyImageView (getDevice context) aiImageView Nothing
VMA.destroyImage (getAllocator context) aiImage aiAllocation
--------------------------------------------
transitionLayout
:: ( HasVulkan context
, MonadUnliftIO m
)
=> context
-> Queues Vk.CommandPool
-> Vk.Image
-> "mip levels" ::: Word32
-> "layer count" ::: Word32
-> Vk.Format
-> "old" ::: Vk.ImageLayout
-> "new" ::: Vk.ImageLayout
-> m ()
transitionLayout ctx pool image mipLevels layerCount format old new =
case (old, new) of
(Vk.IMAGE_LAYOUT_UNDEFINED, Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) ->
oneshot_ ctx pool qTransfer \buf ->
Vk.cmdPipelineBarrier
buf
Vk.PIPELINE_STAGE_TOP_OF_PIPE_BIT
Vk.PIPELINE_STAGE_TRANSFER_BIT
zero
mempty
mempty
( Vector.singleton $
barrier Vk.IMAGE_ASPECT_COLOR_BIT zero Vk.ACCESS_TRANSFER_WRITE_BIT
)
(Vk.IMAGE_LAYOUT_UNDEFINED, Vk.IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) ->
oneshot_ ctx pool qTransfer \buf ->
Vk.cmdPipelineBarrier
buf
Vk.PIPELINE_STAGE_TOP_OF_PIPE_BIT
Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
zero
mempty
mempty
( Vector.singleton $
barrier aspectMask zero $
Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT .|.
Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
)
where
aspectMask =
if hasStencilComponent then
Vk.IMAGE_ASPECT_DEPTH_BIT .|. Vk.IMAGE_ASPECT_STENCIL_BIT
else
Vk.IMAGE_ASPECT_DEPTH_BIT
hasStencilComponent =
format == Vk.FORMAT_D32_SFLOAT_S8_UINT ||
format == Vk.FORMAT_D24_UNORM_S8_UINT
(Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, Vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) ->
oneshot_ ctx pool qGraphics \buf ->
Vk.cmdPipelineBarrier
buf
Vk.PIPELINE_STAGE_TRANSFER_BIT
Vk.PIPELINE_STAGE_FRAGMENT_SHADER_BIT
zero
mempty
mempty
( Vector.singleton $
barrier
Vk.IMAGE_ASPECT_COLOR_BIT
Vk.ACCESS_TRANSFER_WRITE_BIT
Vk.ACCESS_SHADER_READ_BIT
)
_ ->
error $ "Unsupported image layout transfer: " <> show (old, new)
where
barrier aspectMask srcMask dstMask = SomeStruct zero
{ Vk.srcAccessMask = srcMask
, Vk.dstAccessMask = dstMask
, Vk.oldLayout = old
, Vk.newLayout = new
, Vk.srcQueueFamilyIndex = Vk.QUEUE_FAMILY_IGNORED
, Vk.dstQueueFamilyIndex = Vk.QUEUE_FAMILY_IGNORED
, Vk.image = image
, Vk.subresourceRange = subresource aspectMask mipLevels layerCount
}
-- imageBarrier cb oldLayout newLayout aspectMask srcStage srcMask dstStage dstMask image mipLevels layerCount =
-- Vk.cmdPipelineBarrier
-- cb
-- srcStage
-- dstStage
-- zero
-- mempty
-- mempty
-- (Vector.singleton barrier)
-- where
-- barrier = SomeStruct zero
-- { Vk.srcAccessMask = srcMask
-- , Vk.dstAccessMask = dstMask
-- , Vk.oldLayout = oldLayout
-- , Vk.newLayout = newLayout
-- , Vk.srcQueueFamilyIndex = Vk.QUEUE_FAMILY_IGNORED
-- , Vk.dstQueueFamilyIndex = Vk.QUEUE_FAMILY_IGNORED
-- , Vk.image = image
-- , Vk.subresourceRange = subresource aspectMask mipLevels layerCount
-- }
subresource
:: Vk.ImageAspectFlags
-> "mip levels" ::: Word32
-> "layer count" ::: Word32
-> Vk.ImageSubresourceRange
subresource aspectMask mipLevels layerCount = Vk.ImageSubresourceRange
{ aspectMask = aspectMask
, baseMipLevel = 0
, levelCount = mipLevels -- XXX: including base
, baseArrayLayer = 0
, layerCount = layerCount
}
copyBufferToImage
:: ( HasVulkan context
, Foldable t
, Integral deviceSize
, MonadUnliftIO m
)
=> context
-> Queues Vk.CommandPool
-> Vk.Buffer
-> Vk.Image
-> "base extent" ::: Vk.Extent3D
-> "mip offsets" ::: t deviceSize
-> "layer count" ::: Word32
-> m ()
copyBufferToImage ctx pool src dst Vk.Extent3D{..} mipOffsets layerCount =
oneshot_ ctx pool qTransfer \cmd ->
Vk.cmdCopyBufferToImage cmd src dst Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL $
Vector.fromList copyRegions
where
copyRegions = do
(offset, mipLevel) <- zip (toList mipOffsets) [0..]
pure Vk.BufferImageCopy
{ Vk.bufferOffset = fromIntegral offset
, Vk.bufferRowLength = zero -- XXX: "use extent width"
, Vk.bufferImageHeight = zero -- XXX: "use extent height"
, Vk.imageSubresource = Vk.ImageSubresourceLayers
{ aspectMask = Vk.IMAGE_ASPECT_COLOR_BIT
, mipLevel = fromIntegral mipLevel
, baseArrayLayer = 0
, layerCount = layerCount
}
, Vk.imageOffset = zero
, Vk.imageExtent = Vk.Extent3D
{ width = max 1 $ width `shiftR` mipLevel
, height = max 1 $ height `shiftR` mipLevel
, depth = depth
}
}