packages feed

gpu-vulkan-middle-0.1.0.55: src/Gpu/Vulkan/Memory/Middle/Internal.hsc

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE FlexibleContexts, UndecidableInstances #-}
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
{-# LANGUAGE StandaloneDeriving, GeneralizedNewtypeDeriving #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Gpu.Vulkan.Memory.Middle.Internal (
	M(..), mToCore, AllocateInfo(..), allocate, reallocate, reallocate', free,

	group, allocate', free', lookup, Group,

	MapFlags(..), map, unmap,

	Requirements(..), requirementsFromCore,
	Barrier(..), barrierToCore,

	Heap(..), heapFromCore,

	MType(..), mTypeFromCore,
	TypeBits, TypeIndex, elemTypeIndex, typeBitsToTypeIndices
	) where

import Prelude hiding (map, lookup)

import Foreign.Ptr
import Foreign.Marshal.Alloc hiding (free)
import Foreign.Storable
import Foreign.Storable.PeekPoke
import Foreign.C.Enum
import Control.Concurrent.STM
import Control.Concurrent.STM.TSem
import Data.TypeLevel.Maybe qualified as TMaybe
import Data.TypeLevel.ParMaybe qualified as TPMaybe
import Data.Default
import Data.Bits
import Data.Map qualified as M
import Data.IORef
import Data.Word

import Gpu.Vulkan.Enum
import Gpu.Vulkan.Exception.Middle.Internal
import Gpu.Vulkan.Exception.Enum
import Gpu.Vulkan.Memory.Enum

import qualified Gpu.Vulkan.Device.Middle.Types as Device

import qualified Gpu.Vulkan.AllocationCallbacks.Middle.Internal as AllocationCallbacks
import qualified Gpu.Vulkan.Memory.Core as C

#include <vulkan/vulkan.h>

newtype M = M (IORef C.M)

mToCore :: M -> IO C.M
mToCore (M r) = readIORef r

newtype TypeBits = TypeBits #{type uint32_t} deriving (Show, Eq, Bits, FiniteBits)

newtype TypeIndex = TypeIndex Word32
	deriving (Show, Eq, Ord, Enum, Num, Real, Integral)

typeBitsToTypeIndices :: TypeBits -> [TypeIndex]
typeBitsToTypeIndices bs = (fst <$>)
	. filter snd . zip [0 ..] $ testBit bs <$> [0 .. finiteBitSize bs - 1]

elemTypeIndex :: TypeIndex -> TypeBits -> Bool
elemTypeIndex ti tbs = testBit tbs $ fromIntegral ti

data Requirements = Requirements {
	requirementsSize :: Device.Size,
	requirementsAlignment :: Device.Size,
	requirementsMemoryTypeBits :: TypeBits }
	deriving Show

requirementsFromCore :: C.Requirements -> Requirements
requirementsFromCore C.Requirements {
	C.requirementsSize = sz,
	C.requirementsAlignment = al,
	C.requirementsMemoryTypeBits = mtbs } = Requirements {
	requirementsSize = Device.Size sz,
	requirementsAlignment = Device.Size al,
	requirementsMemoryTypeBits = TypeBits mtbs }

data MType = MType {
	mTypePropertyFlags :: PropertyFlags,
	mTypeHeapIndex :: #{type uint32_t} }
	deriving Show

mTypeFromCore :: C.MType -> MType
mTypeFromCore C.MType {
	C.mTypePropertyFlags = pfs,
	C.mTypeHeapIndex = hi } = MType {
	mTypePropertyFlags = PropertyFlagBits pfs,
	mTypeHeapIndex = hi }

data Heap = Heap { heapSize :: Device.Size, heapFlags :: HeapFlags }
	deriving Show

heapFromCore :: C.Heap -> Heap
heapFromCore C.Heap { C.heapSize = sz, C.heapFlags = flgs } =
	Heap { heapSize = Device.Size sz, heapFlags = HeapFlagBits flgs }

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

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

allocateInfoToCore :: WithPoked (TMaybe.M mn) =>
	AllocateInfo mn -> (Ptr C.AllocateInfo -> IO a) -> IO ()
allocateInfoToCore AllocateInfo {
	allocateInfoNext = mnxt,
	allocateInfoAllocationSize = Device.Size sz,
	allocateInfoMemoryTypeIndex = TypeIndex mti } f =
	withPoked' mnxt \pnxt -> withPtrS pnxt \(castPtr -> pnxt') ->
	let	ci = C.AllocateInfo {
			C.allocateInfoSType = (),
			C.allocateInfoPNext = pnxt',
			C.allocateInfoAllocationSize = sz,
			C.allocateInfoMemoryTypeIndex = mti } in
	withPoked ci f

allocate :: WithPoked (TMaybe.M mn) =>
	Device.D -> AllocateInfo mn -> TPMaybe.M AllocationCallbacks.A ma -> IO M
allocate (Device.D dvc) ai mac = M <$> alloca \pm -> do
	allocateInfoToCore ai \pai ->
		AllocationCallbacks.mToCore mac \pac -> do
			r <- C.allocate dvc pai pac pm
			throwUnlessSuccess $ Result r
	newIORef =<< peek pm

-- free :: Device.D -> M -> TPMaybe.M AllocationCallbacks.A mf -> IO ()

group :: Device.D -> TPMaybe.M AllocationCallbacks.A mf ->
	(forall s . Group s k -> IO a) -> IO a
group dvc mac f = do
	(sem, mng) <-atomically $ (,) <$> newTSem 1 <*> newTVar M.empty
	rtn <- f $ Group sem mng
	((\m -> free dvc m mac) `mapM_`) =<< atomically (readTVar mng)
	pure rtn

allocate' :: (Ord k, WithPoked (TMaybe.M mn)) =>
	Device.D -> Group sm k -> k -> AllocateInfo mn ->
	TPMaybe.M AllocationCallbacks.A ma -> IO (Either String M)
allocate' (Device.D dvc) (Group sem ms) k ai mac = do
	ok <- atomically do
		mx <- (M.lookup k) <$> readTVar ms
		case mx of
			Nothing -> waitTSem sem >> pure True
			Just _ -> pure False
	if ok
	then do	m <- M <$> alloca \pm -> do
			allocateInfoToCore ai \pai ->
				AllocationCallbacks.mToCore mac \pac -> do
					r <- C.allocate dvc pai pac pm
					throwUnlessSuccess $ Result r
			newIORef =<< peek pm
		atomically do
			modifyTVar ms (M.insert k m)
			signalTSem sem
		pure $ Right m
	else pure . Left $ "Gpu.Vulkan.Memory.allocate': The key already exist"

data Group s k = Group TSem (TVar (M.Map k M))

free' :: Ord k => Device.D ->
	Group smng k -> k -> TPMaybe.M AllocationCallbacks.A mc ->
	IO (Either String ())
free' dvc (Group sem ms) k mac = do
	mm <- atomically do
		mx <- (M.lookup k) <$> readTVar ms
		case mx of
			Nothing -> pure Nothing
			Just _ -> waitTSem sem >> pure mx
	case mm of
		Nothing -> pure $ Left "Gpu.Vulkan.Memory.free': No such key"
		Just m -> do
			free dvc m mac
			atomically do
				modifyTVar ms (M.delete k)
				signalTSem sem
				pure $ Right ()

lookup :: Ord k => Group sm k -> k -> IO (Maybe M)
lookup (Group _sem ms) k = atomically $ M.lookup k <$> readTVar ms

reallocate :: WithPoked (TMaybe.M mn) =>
	Device.D -> AllocateInfo mn ->
	TPMaybe.M AllocationCallbacks.A ma ->
	M -> IO ()
reallocate d@(Device.D dvc) ai macc m@(M rm) =
	alloca \pm -> allocateInfoToCore ai \pai ->
	AllocationCallbacks.mToCore macc \pac -> do
		r <- C.allocate dvc pai pac pm
		throwUnlessSuccess $ Result r
		free d m macc
		writeIORef rm =<< peek pm

reallocate' :: WithPoked (TMaybe.M mn) =>
	Device.D -> AllocateInfo mn ->
	TPMaybe.M AllocationCallbacks.A ma ->
	M -> IO a -> IO ()
reallocate' (Device.D dvc) ai macc (M rm) act =
	alloca \pm -> allocateInfoToCore ai \pai ->
	AllocationCallbacks.mToCore macc \pac -> do
		r <- C.allocate dvc pai pac pm
		throwUnlessSuccess $ Result r
		mm <- readIORef rm
		writeIORef rm =<< peek pm
		_ <- act
		C.free dvc mm pac

free :: Device.D -> M -> TPMaybe.M AllocationCallbacks.A mf -> IO ()
free (Device.D dvc) (M mem) mac =
	AllocationCallbacks.mToCore mac \pac -> do
		m <- readIORef mem
		C.free dvc m pac

enum "MapFlags" ''#{type VkMemoryMapFlags}
	[''Eq, ''Show, ''Storable, ''Bits] [("MapFlagsZero", 0)]

instance Default MapFlags where def = MapFlagsZero

map :: Device.D -> M -> Device.Size -> Device.Size -> MapFlags ->
	IO (Ptr a)
map (Device.D dvc) (M mem)
	(Device.Size ofst) (Device.Size sz) (MapFlags flgs) = alloca \pd ->
	readIORef mem >>= \m ->
	C.map dvc m ofst sz flgs pd >>= \r ->
	throwUnlessSuccess (Result r) >>
	peek pd

unmap :: Device.D -> M -> IO ()
unmap (Device.D dvc) (M mem) = C.unmap dvc =<< readIORef mem

data Barrier mn = Barrier {
	barrierNext :: TMaybe.M mn,
	barrierSrcAccessMask :: AccessFlags,
	barrierDstAccessMask :: AccessFlags }

deriving instance Show (TMaybe.M mn) => Show (Barrier mn)

barrierToCore :: WithPoked (TMaybe.M mn) => Barrier mn -> (C.Barrier -> IO a) -> IO ()
barrierToCore Barrier {
	barrierNext = mnxt,
	barrierSrcAccessMask = AccessFlagBits sam,
	barrierDstAccessMask = AccessFlagBits dam } f =
	withPoked' mnxt \pnxt -> withPtrS pnxt \(castPtr -> pnxt') -> f C.Barrier {
		C.barrierSType = (),
		C.barrierPNext = pnxt',
		C.barrierSrcAccessMask = sam,
		C.barrierDstAccessMask = dam }