packages feed

gpu-vulkan-0.1.0.137: src/Gpu/Vulkan/Memory.hs

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

module Gpu.Vulkan.Memory (

	-- * ALLOCATE AND BIND

	allocateBind, unsafeReallocateBind, unsafeReallocateBind',

	-- ** Destruction Group

	Group, group, allocateBind', unsafeFree, lookup,

	-- ** MEMORY

	M, getBinded, ImageBuffer(..), ImageBufferBinded(..), ImageBufferArg(..),

	-- ** ALLOCATE INFO

	AllocateInfo(..), M.MType(..), M.TypeBits, M.TypeIndex, M.elemTypeIndex,

	-- ** BINDABLE AND REBINDABLE

	Bindable, Rebindable,

	-- * GET REQUREMENTS

	getRequirementsList, M.Requirements(..),

	-- * READ AND WRITE

	read, write, OffsetSize,

	-- * BARRIER

	M.Barrier(..),

	-- * ENUM

	module Gpu.Vulkan.Memory.Enum

	) where

import Prelude hiding (map, read, lookup)

import Foreign.Ptr
import Foreign.Storable.PeekPoke
import Control.Concurrent.STM
import Control.Exception hiding (try)
import Gpu.Vulkan.Object qualified as VObj
import Data.TypeLevel.Maybe qualified as TMaybe
import Data.TypeLevel.ParMaybe qualified as TPMaybe
import Data.TypeLevel.Tuple.Uncurry
import Data.Maybe
import Data.Map qualified as Map
import Data.HeteroParList qualified as HeteroParList
import Data.IORef

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.Middle as M
import Gpu.Vulkan.Memory.Enum

import Gpu.Vulkan.Memory.Bind
import Gpu.Vulkan.Memory.OffsetSize
import Gpu.Vulkan.Memory.Type
import Gpu.Vulkan.Memory.ImageBuffer

import Control.Monad
import Debug

-- ALLOCATE AND BIND

-- Allocate Bind

allocateBind :: (
	WithPoked (TMaybe.M mn), Bindable ibargs,
	AllocationCallbacks.ToMiddle mac ) =>
	Device.D sd -> HeteroParList.PL (U2 ImageBuffer) ibargs ->
	AllocateInfo mn -> TPMaybe.M (U2 AllocationCallbacks.A) mac ->
	(forall s . HeteroParList.PL (U2 (ImageBufferBinded s)) ibargs ->
		M s ibargs -> IO a) -> IO a
allocateBind dv ibs ai mac f =
	allocate dv ibs ai mac \m -> (`f` m) =<< bindAll dv ibs m 0

allocate :: (
	WithPoked (TMaybe.M n), Alignments ibargs,
	AllocationCallbacks.ToMiddle mac ) =>
	Device.D sd -> HeteroParList.PL (U2 ImageBuffer) ibargs ->
	AllocateInfo n -> TPMaybe.M (U2 AllocationCallbacks.A) mac ->
	(forall s . M s ibargs -> IO a) -> IO a
allocate dv@(Device.D mdv) ibs ai
	(AllocationCallbacks.toMiddle -> mac) f = bracket
	do	mai <- allocateInfoToMiddle dv ibs ai
		M.allocate mdv mai mac
	(\m -> M.free mdv m mac)
	\m -> f =<< newM ibs m

group :: AllocationCallbacks.ToMiddle ma =>
	Device.D sd -> TPMaybe.M (U2 AllocationCallbacks.A) ma ->
	(forall s . Group sd ma s k ibargs -> IO a) -> IO a
group dvc@(Device.D mdvc) ma@(AllocationCallbacks.toMiddle -> mac) f =
	M.group mdvc mac \mmng -> do
		ibargs <- atomically $ newTVar Map.empty
		f $ Group dvc ma ibargs mmng

allocateBind' :: (
	Ord k,
	WithPoked (TMaybe.M mn), Bindable ibargs,
	AllocationCallbacks.ToMiddle ma ) =>
	Group sd ma sm k ibargs -> k ->
	HeteroParList.PL (U2 ImageBuffer) ibargs -> AllocateInfo mn ->
	IO (Either String (
		HeteroParList.PL (U2 (ImageBufferBinded sm)) ibargs,
		M sm ibargs))
allocateBind' (Group dv mac mib mng) k ibs ai = do
	allocate' dv mng k ibs ai mac >>= \case
		Left msg -> pure $ Left msg
		Right m -> do
			rtn@(_, M iibs _) <- (, m) <$> bindAll dv ibs m 0
			atomically $ modifyTVar mib (Map.insert k iibs)
			pure $ Right rtn

allocate' :: (
	Ord k,
	WithPoked (TMaybe.M n), Alignments ibargs,
	AllocationCallbacks.ToMiddle mac ) =>
	Device.D sd -> M.Group sm k -> k ->
	HeteroParList.PL (U2 ImageBuffer) ibargs ->
	AllocateInfo n -> TPMaybe.M (U2 AllocationCallbacks.A) mac ->
	IO (Either String (M sm ibargs))
allocate' dv@(Device.D mdv) mng k ibs ai (AllocationCallbacks.toMiddle -> mac) = do
	mai <- allocateInfoToMiddle dv ibs ai
	(newM ibs `mapM`) =<< M.allocate' mdv mng k mai mac

unsafeFree :: (Ord k, AllocationCallbacks.ToMiddle ma) =>
	Group sd ma smng k ibargs -> k -> IO (Either String ())
unsafeFree (Group (Device.D mdv) (AllocationCallbacks.toMiddle -> mac) _ mng) k =
	M.free' mdv mng k mac

data Group sd ma s k ibargs = Group (Device.D sd)
	(TPMaybe.M (U2 AllocationCallbacks.A) ma)
	(TVar (Map.Map k (IORef (HeteroParList.PL (U2 ImageBuffer) ibargs))))
	(M.Group s k)

lookup :: Ord k =>
	Group sd ma sm k ibargs -> k -> IO (Maybe (M s ibargs))
lookup (Group _ _ ibargss mmng) k = do
	mibargs <- atomically $ Map.lookup k <$> readTVar ibargss
	mmem <- M.lookup mmng k
	pure $ M <$> mibargs <*> mmem

-- Reallocate Bind

unsafeReallocateBind :: (
	WithPoked (TMaybe.M mn), Rebindable ibargs,
	AllocationCallbacks.ToMiddle mac ) =>
	Device.D sd -> HeteroParList.PL (U2 (ImageBufferBinded sm)) ibargs ->
	AllocateInfo mn -> TPMaybe.M (U2 AllocationCallbacks.A) mac ->
	M sm ibargs -> IO ()
unsafeReallocateBind dv ibs ai mac m =
	reallocate dv ibs ai mac m >> rebindAll dv ibs m 0

unsafeReallocateBind' :: (
	WithPoked (TMaybe.M mn), Rebindable ibargs,
	AllocationCallbacks.ToMiddle mac ) =>
	Device.D sd -> HeteroParList.PL (U2 (ImageBufferBinded sm)) ibargs ->
	AllocateInfo mn -> TPMaybe.M (U2 AllocationCallbacks.A) mac ->
	M sm ibargs -> IO a -> IO ()
unsafeReallocateBind' dv ibs ai mac m act =
	reallocate' dv ibs ai mac m $ rebindAll dv ibs m 0 >> act

reallocate :: (
	WithPoked (TMaybe.M n), Alignments ibargs,
	AllocationCallbacks.ToMiddle mac ) =>
	Device.D sd -> HeteroParList.PL (U2 (ImageBufferBinded sm)) ibargs ->
	AllocateInfo n -> TPMaybe.M (U2 AllocationCallbacks.A) mac ->
	M sm ibargs -> IO ()
reallocate dv@(Device.D mdv) ibs ai (AllocationCallbacks.toMiddle -> mac) m = do
	mai <- reallocateInfoToMiddle dv ibs ai
	(_, mm) <- readM m
	M.reallocate mdv mai mac mm
	writeMBinded m ibs

reallocate' :: (
	WithPoked (TMaybe.M n), Alignments ibargs,
	AllocationCallbacks.ToMiddle mac ) =>
	Device.D sd -> HeteroParList.PL (U2 (ImageBufferBinded sm)) ibargs ->
	AllocateInfo n -> TPMaybe.M (U2 AllocationCallbacks.A) mac ->
	M sm ibargs -> IO a -> IO ()
reallocate' dv@(Device.D mdv) ibs ai (AllocationCallbacks.toMiddle -> mac) m act = do
	mai <- reallocateInfoToMiddle dv ibs ai
	(_, mm) <- readM m
	M.reallocate' mdv mai mac mm $ writeMBinded m ibs >> act

-- Allocate Info

data AllocateInfo mn = AllocateInfo {
	allocateInfoNext :: TMaybe.M mn,
	allocateInfoMemoryTypeIndex :: M.TypeIndex }

deriving instance Show (TMaybe.M mn) => Show (AllocateInfo mn)
deriving instance Eq (TMaybe.M mn) => Eq (AllocateInfo mn)

allocateInfoToMiddle :: forall sd ibargs mn . Alignments ibargs =>
	Device.D sd -> HeteroParList.PL (U2 ImageBuffer) ibargs ->
	AllocateInfo mn -> IO (M.AllocateInfo mn)
allocateInfoToMiddle dv ibs AllocateInfo {
	allocateInfoNext = mnxt,
	allocateInfoMemoryTypeIndex = mti } = do
	reqss <- getRequirementsList dv ibs
	pure M.AllocateInfo {
		M.allocateInfoNext = mnxt,
		M.allocateInfoAllocationSize = memoryRequirementsListToSize
			0 (alignments @ibargs) reqss,
		M.allocateInfoMemoryTypeIndex = mti }

reallocateInfoToMiddle :: forall sd sm ibargs mn . Alignments ibargs =>
	Device.D sd -> HeteroParList.PL (U2 (ImageBufferBinded sm)) ibargs ->
	AllocateInfo mn -> IO (M.AllocateInfo mn)
reallocateInfoToMiddle dv ibs AllocateInfo {
	allocateInfoNext = mnxt,
	allocateInfoMemoryTypeIndex = mti } = do
	reqss <- getRequirementsListBinded dv ibs
	pure M.AllocateInfo {
		M.allocateInfoNext = mnxt,
		M.allocateInfoAllocationSize = memoryRequirementsListToSize
			0 (alignments @ibargs) reqss,
		M.allocateInfoMemoryTypeIndex = mti }

memoryRequirementsListToSize ::
	Device.M.Size -> [Maybe Device.M.Size] -> [M.Requirements] -> Device.M.Size
memoryRequirementsListToSize sz0 _ [] = sz0
memoryRequirementsListToSize sz0 [] _ = sz0
memoryRequirementsListToSize sz0 (malgn : malgns) (reqs : reqss) =
	memoryRequirementsListToSize
		(((sz0 - 1) `div` algn + 1) * algn + sz) malgns reqss
	where
	sz = M.requirementsSize reqs
	algn = (fromMaybe 1 malgn) `lcm`
		M.requirementsAlignment reqs

-- READ AND WRITE

read :: forall nm obj i v sd sm ibargs .
	(VObj.Store v obj, OffsetSize nm obj ibargs i) =>
	Device.D sd -> M sm ibargs -> M.MapFlags -> IO v
read dv m flgs = bracket
	(map @nm @obj @i dv m flgs) (const $ unmap dv m)
	\(ptr :: Ptr (VObj.TypeOf obj)) ->
		VObj.load @_ @obj ptr =<< objectLength @nm @obj m

write :: forall nm obj i sd sm ibargs v .
	(VObj.Store v obj, OffsetSize nm obj ibargs i) =>
	Device.D sd -> M sm ibargs -> M.MapFlags -> v -> IO ()
write dv m flgs v = bracket
	(map @nm @obj @i dv m flgs) (const $ unmap dv m)
	\(ptr :: Ptr (VObj.TypeOf obj)) -> do
		ln <- objectLength @nm @obj m
		VObj.store @_ @obj ptr ln v

map :: forall nm obj i sd sm ibargs . OffsetSize nm obj ibargs i =>
	Device.D sd -> M sm ibargs -> M.MapFlags ->
	IO (Ptr (VObj.TypeOf obj))
map dv@(Device.D mdv) m flgs = readM m >>= \(_, mm) -> do
	(ost, sz) <- offsetSize @nm @obj @_ @i dv m 0
	when debug $ print (ost, sz)
	M.map mdv mm ost sz flgs

unmap :: Device.D sd -> M sm ibargs -> IO ()
unmap (Device.D mdv) m = M.unmap mdv . snd =<< readM m