packages feed

gpu-vulkan-0.1.0.154: src/Gpu/Vulkan/Buffer/Internal.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ScopedTypeVariables, RankNTypes, TypeApplications #-}
{-# LANGUAGE GADTs, TypeFamilies #-}
{-# LANGUAGE DataKinds, PolyKinds #-}
{-# LANGUAGE KindSignatures, TypeOperators #-}
{-# LANGUAGE MultiParamTypeClasses, AllowAmbiguousTypes #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleContexts, FlexibleInstances, UndecidableInstances #-}
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Gpu.Vulkan.Buffer.Internal (

	-- * CREATE

	create, B, CreateInfo(..),

	-- ** Buffer Group

	Group, group, create', unsafeDestroy, lookup,

	-- * BINDED

	getMemoryRequirements, Binded, lengthBinded,
	IndexedForList(..), indexedListToMiddles, indexedListToMiddle,

	-- * COPY

	MakeCopies(..), CopyInfo,

	-- * IMAGE COPY

	ImageCopy(..), ImageCopyListToMiddle(..),

	-- * MEMORY BARRIER

	MemoryBarrier(..), MemoryBarrierListToMiddle(..),

	-- * UNSAFE

	unsafeToBinded
	
	) where

import Prelude hiding (lookup)
import GHC.TypeLits
import Foreign.Storable.PeekPoke
import Control.Exception
import Gpu.Vulkan.Object qualified as VObj
import Data.Kind
import Data.TypeLevel.Tuple.Uncurry
import Data.TypeLevel.Tuple.MapIndex qualified as TMapIndex
import Data.TypeLevel.Maybe qualified as TMaybe
import Data.TypeLevel.ParMaybe qualified as TPMaybe
import Data.HeteroParList qualified as HeteroParList
import Data.HeteroParList (pattern (:**))
import Data.Word

import Gpu.Vulkan.Enum hiding (ObjectType)
import Gpu.Vulkan.Buffer.Enum

import qualified Gpu.Vulkan.Middle as C
import qualified Gpu.Vulkan.AllocationCallbacks as AllocationCallbacks
import qualified Gpu.Vulkan.AllocationCallbacks.Type as AllocationCallbacks
import qualified Gpu.Vulkan.Device.Type as Device
import qualified Gpu.Vulkan.Device.Middle as Device.M
import qualified Gpu.Vulkan.Memory as Memory
import qualified Gpu.Vulkan.Buffer.Middle as M
import qualified Gpu.Vulkan.Buffer.Middle as C
import qualified Gpu.Vulkan.QueueFamily as QueueFamily
import qualified Gpu.Vulkan.Image as Image.M

import Gpu.Vulkan.Buffer.Type

import Control.Concurrent.STM
import Control.Concurrent.STM.TSem
import Data.Map qualified as Map

data CreateInfo mn objs = CreateInfo {
	createInfoNext :: TMaybe.M mn,
	createInfoFlags :: CreateFlags,
	createInfoLengths :: HeteroParList.PL VObj.Length objs,
	createInfoUsage :: UsageFlags,
	createInfoSharingMode :: SharingMode,
	createInfoQueueFamilyIndices :: [QueueFamily.Index] }

deriving instance
	(Show (TMaybe.M n), Show (HeteroParList.PL VObj.Length objs)) =>
	Show (CreateInfo n objs)

createInfoToMiddle :: VObj.SizeAlignmentList objs =>
	CreateInfo n objs -> M.CreateInfo n
createInfoToMiddle CreateInfo {
	createInfoNext = mnxt,
	createInfoFlags = flgs,
	createInfoLengths = lns,
	createInfoUsage = usg,
	createInfoSharingMode = smd,
	createInfoQueueFamilyIndices = qfis } = M.CreateInfo {
	M.createInfoNext = mnxt,
	M.createInfoFlags = flgs,
	M.createInfoSize = VObj.wholeSize lns,
	M.createInfoUsage = usg,
	M.createInfoSharingMode = smd,
	M.createInfoQueueFamilyIndices = qfis }

create :: (
	WithPoked (TMaybe.M mn),
	VObj.SizeAlignmentList objs, AllocationCallbacks.ToMiddle ma ) =>
	Device.D sd -> CreateInfo mn objs ->
	TPMaybe.M (U2 AllocationCallbacks.A) ma ->
	(forall sb . B sb nm objs -> IO a) -> IO a
create (Device.D dvc) ci (AllocationCallbacks.toMiddle -> mac) f = bracket
	(M.create dvc (createInfoToMiddle ci) mac) (\b -> M.destroy dvc b mac)
	(f . B (createInfoLengths ci))

data Group sd ma s k nm objs = Group (Device.D sd)
	(TPMaybe.M (U2 AllocationCallbacks.A) ma)
	TSem (TVar (Map.Map k (B s nm objs)))

group :: AllocationCallbacks.ToMiddle md =>
	Device.D sd -> TPMaybe.M (U2 AllocationCallbacks.A) md ->
	(forall s . Group sd md s k nm objs -> IO a) -> IO a
group dvc@(Device.D mdvc) mac@(AllocationCallbacks.toMiddle -> mmac) f = do
	(sem, m) <- atomically $ (,) <$> newTSem 1 <*> newTVar Map.empty
	rtn <- f $ Group dvc mac sem m
	((\(B _ mb) -> M.destroy mdvc mb mmac) `mapM_`)
		=<< atomically (readTVar m)
	pure rtn

create' :: (
	Ord k, WithPoked (TMaybe.M mn),
	VObj.SizeAlignmentList objs, AllocationCallbacks.ToMiddle ma ) =>
	Group sd ma sg k nm objs -> k -> CreateInfo mn objs ->
	IO (Either String (B sg nm objs))
create' (Group (Device.D dvc)
	(AllocationCallbacks.toMiddle -> mac) sem bs) k ci = do
	ok <- atomically do
		mx <- (Map.lookup k) <$> readTVar bs
		case mx of
			Nothing -> waitTSem sem >> pure True
			Just _ -> pure False
	if ok
	then do	b <- M.create dvc (createInfoToMiddle ci) mac
		let b' = B (createInfoLengths ci) b
		atomically $ modifyTVar bs (Map.insert k b') >> signalTSem sem
		pure $ Right b'
	else pure . Left $ "Gpu.Vulkan.Buffer.create': The key already exist"

unsafeDestroy :: (Ord k, AllocationCallbacks.ToMiddle ma) =>
	Group sd ma sg k nm objs -> k -> IO (Either String ())
unsafeDestroy (Group (Device.D mdvc)
	(AllocationCallbacks.toMiddle -> ma) sem bs) k = do
	mb <- atomically do
		mx <- Map.lookup k <$> readTVar bs
		case mx of
			Nothing -> pure Nothing
			Just _ -> waitTSem sem >> pure mx
	case mb of
		Nothing -> pure $ Left "Gpu.Vulkan.Buffer.unsafeDestroy: No such key"
		Just (B _ b) -> do
			M.destroy mdvc b ma
			atomically do
				modifyTVar bs (Map.delete k)
				signalTSem sem
				pure $ Right ()

lookup :: Ord k => Group sd md sg k nm objs -> k -> IO (Maybe (B sg nm objs))
lookup (Group _ _ _sem bs) k = atomically $ Map.lookup k <$> readTVar bs

getMemoryRequirements :: Device.D sd -> B sb nm objs -> IO Memory.Requirements
getMemoryRequirements (Device.D dvc) (B _ b) = M.getMemoryRequirements dvc b

data IndexedForList sm sb nm t onm = forall objs .
	VObj.OffsetOfList t onm objs => IndexedForList (Binded sm sb nm objs)

indexedListToOffset :: forall sm sb nm v onm a . IndexedForList sm sb nm v onm ->
	(forall vs . (Binded sm sb nm vs, Device.M.Size) -> a) -> a
indexedListToOffset (IndexedForList b@(Binded lns _)) f =
	f (b, fst $ VObj.offsetOfList @v @onm lns)

indexedListToMiddle :: IndexedForList sm sb nm v onm -> (M.B, Device.M.Size)
indexedListToMiddle il = indexedListToOffset il \(Binded _ b, sz) -> (b, sz)

indexedListToMiddles ::
	HeteroParList.PL (U5 IndexedForList) smsbvs -> [(M.B, Device.M.Size)]
indexedListToMiddles HeteroParList.Nil = []
indexedListToMiddles (U5 il :** ils) =
	indexedListToMiddle il : indexedListToMiddles ils

class CopyPrefix (area :: [VObj.O]) (src :: [VObj.O]) (dst :: [VObj.O]) where
	copyCheckLengthPrefix ::
		HeteroParList.PL VObj.Length src ->
		HeteroParList.PL VObj.Length dst -> Bool
	copySizePrefix :: Word64 -> HeteroParList.PL VObj.Length src -> Word64

instance CopyPrefix '[] src dst where
	copyCheckLengthPrefix _ _ = True
	copySizePrefix sz _ = sz

instance (
	VObj.SizeAlignment a,
	CopyPrefix as ss ds ) =>
	CopyPrefix (a ': as) (a ': ss) (a ': ds) where
	copyCheckLengthPrefix (s :** ss) (d :** ds) =
		s == d && copyCheckLengthPrefix @as ss ds
	copySizePrefix sz (ln :** lns) = copySizePrefix @as @ss @ds
		(((sz - 1) `div` algn + 1) * algn + fromIntegral (VObj.size ln))
		lns
		where algn = fromIntegral $ VObj.alignment @a

class CopyInfo (area :: [VObj.O]) (is :: Nat) (id :: Nat) (src :: [VObj.O]) (dst :: [VObj.O]) where
	copyCheckLength ::
		HeteroParList.PL VObj.Length src ->
		HeteroParList.PL VObj.Length dst -> Bool
	copySrcOffset :: Word64 -> HeteroParList.PL VObj.Length src -> Word64
	copyDstOffset :: Word64 -> HeteroParList.PL VObj.Length dst -> Word64
	copySize :: HeteroParList.PL VObj.Length src -> Word64

type OT o = VObj.TypeOf o

instance (
	Sizable (VObj.TypeOf a),
	CopyPrefix (a ': as) (a ': ss) (a ': ds) ) => CopyInfo (a ': as) 0 0 (a ': ss) (a ': ds) where
	copyCheckLength = copyCheckLengthPrefix @(a ': as) @(a ': ss) @(a ': ds)
	copySrcOffset ost _ = ((ost - 1) `div` algn + 1) * algn
		where algn = fromIntegral $ alignment' @(OT a)
	copyDstOffset ost _ = ((ost - 1) `div` algn + 1) * algn
		where algn = fromIntegral $ alignment' @(OT a)
	copySize = copySizePrefix @(a ': as) @(a ': ss) @(a ': ds) 0

instance {-# OVERLAPPABLE #-}
	(VObj.SizeAlignment a, CopyInfo (a ': as) 0 (id - 1) (a ': ss) ds) =>
	CopyInfo (a ': as) 0 id (a ': ss) (a ': ds) where
	copyCheckLength ss (_ :** ds) =
		copyCheckLength @(a ': as) @0 @(id - 1) @(a ': ss) @ds ss ds
	copySrcOffset ost lns = copySrcOffset @(a ': as) @0 @(id - 1) @(a ': ss) @ds ost lns
	copyDstOffset ost (ln :** lns) = copyDstOffset @(a ': as) @0 @(id - 1) @(a ': ss)
		(((ost - 1) `div` algn + 1) * algn + fromIntegral (VObj.size ln))
		lns
		where algn = fromIntegral $ VObj.alignment @a
	copySize = copySize @(a ': as) @0 @(id - 1) @(a ': ss) @ds

instance {-# OVERLAPPABLE #-}
	(VObj.SizeAlignment d, CopyInfo (a ': as) 0 id (a ': ss) ds) =>
	CopyInfo (a ': as) 0 id (a ': ss) (d ': ds) where
	copyCheckLength ss (_ :** ds) =
		copyCheckLength @(a ': as) @0 @id @(a ': ss) @ds ss ds
	copySrcOffset ost lns = copySrcOffset @(a ': as) @0 @id @(a ': ss) @ds ost lns
	copyDstOffset ost (ln :** lns) = copyDstOffset @(a ': as) @0 @id @(a ': ss)
		(((ost - 1) `div` algn + 1) * algn + fromIntegral (VObj.size ln))
		lns
		where algn = fromIntegral $ VObj.alignment @d
	copySize = copySize @(a ': as) @0 @id @(a ': ss) @ds

instance {-# OVERLAPPABLE #-}
	(VObj.SizeAlignment a, CopyInfo (a ': as) (is - 1) id ss ds) =>
	CopyInfo (a ': as) is id (a ': ss) ds where
	copyCheckLength (_ :** ss) ds = copyCheckLength @(a ': as) @(is - 1) @id @ss @ds ss ds
	copySrcOffset ost (ln :** lns) = copySrcOffset @(a ': as) @(is - 1) @id @ss @ds
		(((ost - 1) `div` algn + 1) * algn + fromIntegral (VObj.size ln))
		lns
		where algn = fromIntegral $ VObj.alignment @a
	copyDstOffset ost lns = copyDstOffset @(a ': as) @(is - 1) @id @ss ost lns
	copySize (_ :** lns) = copySize @(a ': as) @(is - 1) @id @ss @ds lns

instance {-# OVERLAPPABLE #-}
	(VObj.SizeAlignment s,
	CopyInfo as is id ss ds) =>
	CopyInfo as is id (s ': ss) ds where
	copyCheckLength (_ :** ss) ds = copyCheckLength @as @is @id ss ds
	copySrcOffset ost (ln :** lns) = copySrcOffset @as @is @id @ss @ds
		(((ost - 1) `div` algn + 1) * algn + fromIntegral (VObj.size ln))
		lns
		where algn = fromIntegral (VObj.alignment @s)
	copyDstOffset ost lns = copyDstOffset @as @is @id @ss ost lns
	copySize (_ :** lns) = copySize @as @is @id @ss @ds lns

makeCopy :: forall (as :: [VObj.O]) is id ss ds . CopyInfo as is id ss ds =>
	HeteroParList.PL VObj.Length ss -> HeteroParList.PL VObj.Length ds -> C.Copy
makeCopy src dst
	| copyCheckLength @as @is @id src dst = C.Copy {
		C.copySrcOffset = copySrcOffset @as @is @id @ss @ds 0 src,
		C.copyDstOffset = copyDstOffset @as @is @id @ss @ds 0 dst,
		C.copySize = copySize @as @is @id @ss @ds src }
	| otherwise = error "List lengths are different"

class MakeCopies (cpss :: [([VObj.O], Nat, Nat)]) (ss :: [VObj.O]) (ds :: [VObj.O]) where
	makeCopies ::
		HeteroParList.PL VObj.Length ss ->
		HeteroParList.PL VObj.Length ds -> [C.Copy]

instance MakeCopies '[] ss ds where makeCopies _ _ = []

instance (CopyInfo as is id ss ds, MakeCopies ass ss ds) =>
	MakeCopies ('(as, is, id) ': ass) ss ds where
	makeCopies src dst = makeCopy @as @is @id src dst : makeCopies @ass src dst

{-
offsetSize :: forall v vs . (
	VObj.OffsetRange v vs, VObj.SizeAlignment v,
	VObj.LengthOf v vs ) =>
	HeteroParList.PL VObj.Length vs ->
	Device.M.Size -> (Device.M.Size, Device.M.Size)
offsetSize lns _ = (VObj.offsetNew @v lns, sizeNew @v lns)

sizeNew :: forall v vs . (
	VObj.SizeAlignment v, VObj.LengthOf v vs ) =>
	HeteroParList.PL VObj.Length vs -> Device.M.Size
sizeNew = fromIntegral . VObj.size . VObj.objectLengthOf @v
-}

data MemoryBarrier mn sm sb nm obj = forall objs . (
	VObj.OffsetRange obj objs 0, VObj.LengthOf obj objs ) =>
	MemoryBarrier {
		memoryBarrierNext :: TMaybe.M mn,
		memoryBarrierSrcAccessMask :: AccessFlags,
		memoryBarrierDstAccessMask :: AccessFlags,
		memoryBarrierSrcQueueFamilyIndex :: QueueFamily.Index,
		memoryBarrierDstQueueFamilyIndex :: QueueFamily.Index,
		memoryBarrierBuffer :: Binded sm sb nm objs }

memoryBarrierToMiddle :: forall n sm sb nm obj .
	MemoryBarrier n sm sb nm obj -> M.MemoryBarrier n
memoryBarrierToMiddle MemoryBarrier {
	memoryBarrierNext = mnxt,
	memoryBarrierSrcAccessMask = sam,
	memoryBarrierDstAccessMask = dam,
	memoryBarrierSrcQueueFamilyIndex = sqfi,
	memoryBarrierDstQueueFamilyIndex = dqfi,
	memoryBarrierBuffer = Binded lns b :: Binded sm sb nm objs } =
	M.MemoryBarrier {
		M.memoryBarrierNext = mnxt,
		M.memoryBarrierSrcAccessMask = sam,
		M.memoryBarrierDstAccessMask = dam,
		M.memoryBarrierSrcQueueFamilyIndex = sqfi,
		M.memoryBarrierDstQueueFamilyIndex = dqfi,
		M.memoryBarrierBuffer = b,
		M.memoryBarrierOffset = ost,
		M.memoryBarrierSize = sz }
	where (ost, sz) = VObj.offsetRange @obj @_ @0 0 lns

class MemoryBarrierListToMiddle nsmsbnmobjs where
	memoryBarrierListToMiddle ::
		HeteroParList.PL (U5 MemoryBarrier) nsmsbnmobjs ->
		HeteroParList.PL M.MemoryBarrier (TMapIndex.M0_5 nsmsbnmobjs)

instance MemoryBarrierListToMiddle '[] where
	memoryBarrierListToMiddle HeteroParList.Nil = HeteroParList.Nil

instance MemoryBarrierListToMiddle nsmsbnmobjs =>
	MemoryBarrierListToMiddle ('(mn, sm, sb, nm, obj) ': nsmsbnmobjs) where
	memoryBarrierListToMiddle (U5 mb :** mbs) =
		memoryBarrierToMiddle mb :** memoryBarrierListToMiddle mbs

data ImageCopy img inm = ImageCopy {
	imageCopyImageSubresource :: Image.M.SubresourceLayers,
	imageCopyImageOffset :: C.Offset3d,
	imageCopyImageExtent :: C.Extent3d }
	deriving Show

class ImageCopyListToMiddle algn objs (img :: Type) (inms :: [Symbol]) where
	imageCopyListToMiddle ::
		Binded sm sb nm objs ->
		HeteroParList.PL (ImageCopy img) inms ->
		[M.ImageCopy]

instance ImageCopyListToMiddle algn objs img '[] where
	imageCopyListToMiddle _ HeteroParList.Nil = []

instance (
	VObj.OffsetRange (VObj.Image algn img nm) objs 0,
	VObj.LengthOf (VObj.Image algn img nm) objs,
	ImageCopyListToMiddle algn objs img nms ) =>
	ImageCopyListToMiddle algn objs img (nm ': nms) where
	imageCopyListToMiddle bf (ic :** ics) =
		imageCopyToMiddle @algn @_ @nm bf (ic :: ImageCopy img nm) :
		imageCopyListToMiddle @algn bf ics

imageCopyToMiddle :: forall algn img inm sm sb nm obj objs . (
	obj ~ VObj.Image algn img inm,
	VObj.OffsetRange obj objs 0, VObj.LengthOf obj objs ) =>
	Binded sm sb nm objs -> ImageCopy img inm -> M.ImageCopy
imageCopyToMiddle (Binded lns _) ImageCopy {
	imageCopyImageSubresource = isr,
	imageCopyImageOffset = iost,
	imageCopyImageExtent = iext } = M.ImageCopy {
	M.imageCopyBufferOffset = ost,
	M.imageCopyBufferRowLength = fromIntegral r,
	M.imageCopyBufferImageHeight = fromIntegral h,
	M.imageCopyImageSubresource = isr,
	M.imageCopyImageOffset = iost,
	M.imageCopyImageExtent = iext }
	where
	(ost, _) = VObj.offsetRange @(VObj.Image algn img inm) @_ @0 0 lns
	VObj.LengthImage r _w h _d _lc = VObj.lengthOf @obj lns