packages feed

gpu-vulkan-0.1.0.137: src/Gpu/Vulkan/Pipeline/Graphics.hs

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

module Gpu.Vulkan.Pipeline.Graphics (

	-- * CREATE AND RECREATE

	createGs, unsafeRecreateGs, G, CreateInfo(..),
	CreateInfoListArgsToGArgs, CreateInfoListToMiddle,

	-- ** Group

	group, Group, createGs', unsafeDestroyGs, lookup

	) where

import Prelude hiding (lookup)
import Foreign.Storable.PeekPoke
import Control.Concurrent.STM
import Control.Concurrent.STM.TSem
import Control.Exception
import Data.Kind
import Data.TypeLevel.Maybe qualified as TMaybe
import Data.TypeLevel.ParMaybe qualified as TPMaybe
import Data.TypeLevel.Tuple.Uncurry
import Data.TypeLevel.Tuple.Index qualified as TIndex
import Data.TypeLevel.Tuple.MapIndex qualified as TMapIndex
import Data.Map qualified as Map
import Data.HeteroParList qualified as HeteroParList
import Data.HeteroParList (pattern (:**))
import Data.HeteroParList.Tuple qualified as HeteroParList
import Data.Word
import Data.Int

import Language.SpirV.ShaderKind

import Gpu.Vulkan.Pipeline.Enum
import Gpu.Vulkan.Pipeline.Graphics.Type

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.Pipeline.Graphics.Middle as M
import qualified Gpu.Vulkan.PipelineCache.Type as Cache
import qualified Gpu.Vulkan.PipelineLayout.Type as Layout

import qualified Gpu.Vulkan.RenderPass.Type as RenderPass
import qualified Gpu.Vulkan.Pipeline.DynamicState as DynamicState
import qualified Gpu.Vulkan.Pipeline.ColorBlendState as ColorBlendState
import qualified Gpu.Vulkan.Pipeline.DepthStencilState as DepthStencilState
import qualified Gpu.Vulkan.Pipeline.MultisampleState as MultisampleState
import qualified Gpu.Vulkan.Pipeline.RasterizationState as RasterizationState
import qualified Gpu.Vulkan.Pipeline.ViewportState as ViewportState
import qualified Gpu.Vulkan.Pipeline.TessellationState as TessellationState
import qualified Gpu.Vulkan.Pipeline.InputAssemblyState as InputAssemblyState
import Gpu.Vulkan.Pipeline.VertexInputState.Internal
	qualified as VertexInputState
import qualified Gpu.Vulkan.Pipeline.ShaderStage.Internal as ShaderStage

import Gpu.Vulkan.VertexInput qualified as VertexInput

import qualified Gpu.Vulkan.Pipeline.ShaderStage.Middle as ShaderStage.M

-- CREATE AND RECREATE

createGs :: (CreateInfoListToMiddle cias, AllocationCallbacks.ToMiddle mac) =>
	Device.D sd -> Maybe (Cache.P sc) ->
	HeteroParList.PL (U14 CreateInfo) cias ->
	TPMaybe.M (U2 AllocationCallbacks.A) mac ->
	(forall sg .
		HeteroParList.PL
			(U3 (G sg)) (CreateInfoListArgsToGArgs cias) ->
		IO a) -> IO a
createGs d@(Device.D dvc) ((Cache.pToMiddle <$>) -> mc) cis
	macc@(AllocationCallbacks.toMiddle -> macd) f = bracket
	(createInfoListToMiddle d cis >>= \cis' -> M.createGs dvc mc cis'
			(AllocationCallbacks.toMiddle macc)
		<* destroyShaderStages' d cis' (allocationCallbacksListListFromCreateInfoList cis))
	(\gs -> M.destroyGs dvc gs macd) (f . gListFromMiddle)

class GListFromMiddle ss where
	gListFromMiddle :: [M.G] -> HeteroParList.PL (U3 (G sg)) ss
instance GListFromMiddle '[] where
	gListFromMiddle = \case
		[] -> HeteroParList.Nil
		_ : _ -> error "Wrong number of elements"

instance GListFromMiddle ss => GListFromMiddle ('(s, t, foo) ': ss) where
	gListFromMiddle = \case
		g : gs -> U3 (G @_ @s @t g) :** gListFromMiddle @ss gs
		[] -> error "Wrong number of elements"

unsafeRecreateGs :: (CreateInfoListToMiddle cias, AllocationCallbacks.ToMiddle mac) =>
	Device.D sd -> Maybe (Cache.P s) -> HeteroParList.PL (U14 CreateInfo) cias ->
	TPMaybe.M (U2 AllocationCallbacks.A) mac ->
	HeteroParList.PL (U3 (G sg)) (CreateInfoListArgsToGArgs cias) -> IO ()
unsafeRecreateGs d@(Device.D dvc) ((Cache.pToMiddle <$>) -> mc) cis macc gpls = do
	cis' <- createInfoListToMiddle d cis
	M.recreateGs dvc mc cis'
		(AllocationCallbacks.toMiddle macc) $ gListToMiddle gpls
	destroyShaderStages' d cis' $ allocationCallbacksListListFromCreateInfoList cis

gListToMiddle :: HeteroParList.PL (U3 (G sg)) ss -> [M.G]
gListToMiddle = HeteroParList.toList $ \(U3 (G g)) -> g

-- CREATE INFO

data CreateInfo
	mn ssas visa iasa tsssa vpsa rstsa mssa dssa cbsa dsa lyta rpa bpha =
	CreateInfo {
		createInfoNext :: TMaybe.M mn,
		createInfoFlags :: CreateFlags,
		createInfoStages ::
			HeteroParList.PL (U5 ShaderStage.CreateInfo) ssas,
		createInfoVertexInputState ::
			Maybe (U3 VertexInputState.CreateInfo visa),
		createInfoInputAssemblyState ::
			Maybe (InputAssemblyState.CreateInfo iasa),
		createInfoTessellationState ::
			Maybe (TessellationState.CreateInfo tsssa),
		createInfoViewportState :: Maybe (ViewportState.CreateInfo vpsa),
		createInfoRasterizationState ::
			Maybe (RasterizationState.CreateInfo rstsa),
		createInfoMultisampleState ::
			Maybe (MultisampleState.CreateInfo mssa),
		createInfoDepthStencilState ::
			Maybe (DepthStencilState.CreateInfo dssa),
		createInfoColorBlendState ::
			Maybe (ColorBlendState.CreateInfo cbsa),
		createInfoDynamicState :: Maybe (DynamicState.CreateInfo dsa),
		createInfoLayout :: U3 Layout.P lyta,
		createInfoRenderPass :: RenderPass.R rpa,
		createInfoSubpass :: Word32,
		createInfoBasePipelineHandle :: Maybe (U4 G bpha),
		createInfoBasePipelineIndex :: Int32 }

-- CREATE INFO LIST ARGS TO G ARGS

type family CreateInfoListArgsToGArgs cias where
	CreateInfoListArgsToGArgs '[] = '[]
	CreateInfoListArgsToGArgs (cia ': cias) =
		CreateInfoArgsToGArgs cia ': CreateInfoListArgsToGArgs cias

type family CreateInfoArgsToGArgs cia where
	CreateInfoArgsToGArgs '(
		n, ssas, '(nv, vibs, vias), iasa, tsssa, vpsa, rstsa, mssa, dssa, cbsa, dsa,
		slbtss, rpa, bpha ) = '(vibs, vias, slbtss)

-- CREATE INFO LIST TO MIDDLE

class (	GListFromMiddle (CreateInfoListArgsToGArgs cias),
	DestroyShaderStages
		(MiddleArgs cias) (TAllocationCallbacksListList cias),
	AllocationCallbacksListListFromCreateInfoList cias,
	M.CreateInfoListToCore (MiddleArgs cias) ) =>
	CreateInfoListToMiddle cias where
	type MiddleArgs cias :: [(
		Maybe Type, [(Maybe Type, ShaderKind, [Type])], Maybe Type,
		Maybe Type, Maybe Type, Maybe Type, Maybe Type, Maybe Type,
		Maybe Type, Maybe Type, Maybe Type)]
	createInfoListToMiddle ::
		Device.D sd -> HeteroParList.PL (U14 CreateInfo) cias ->
		IO (HeteroParList.PL (U11 M.CreateInfo) (MiddleArgs cias))

instance CreateInfoListToMiddle '[] where
	type MiddleArgs '[] = '[]
	createInfoListToMiddle _ HeteroParList.Nil = pure HeteroParList.Nil

instance (

	HeteroParList.Map3_5 ssas,
	ShaderStage.CreateInfoListToMiddle ssas,
	ShaderStage.M.CreateInfoListToCore (ShaderStage.MiddleArgs ssas),
	VertexInputState.BindingStrideList vibs VertexInput.Rate,
	VertexInputState.AttributeDescriptions vibs vias,
	WithPoked (TMaybe.M mn), WithPoked (TMaybe.M nvis),
	WithPoked (TMaybe.M iasa), WithPoked (TMaybe.M tsssa),
	WithPoked (TMaybe.M vpsa), WithPoked (TMaybe.M rstsa),
	WithPoked (TMaybe.M mssa), WithPoked (TMaybe.M dssa),
	WithPoked (TMaybe.M cbsa), WithPoked (TMaybe.M dsa),
	CreateInfoListToMiddle ss ) =>
	CreateInfoListToMiddle ('(
		mn, ssas, '(nvis, vibs, vias), iasa, tsssa, vpsa, rstsa, mssa, dssa, cbsa, dsa,
		'(sl, sbtss, pcl), rpa, '(sb, vibs', vias', slbtss') ) ': ss)  where
	type MiddleArgs ('(
		mn, ssas, '(nvis, vibs, vias), iasa, tsssa, vpsa, rstsa, mssa, dssa, cbsa, dsa,
		'(sl, sbtss, pcl), rpa, '(sb, vibs', vias', slbtss') ) ': ss) =
		'(mn, ShaderStage.MiddleArgs ssas, nvis, iasa, tsssa, vpsa, rstsa,
			mssa, dssa, cbsa, dsa) ': MiddleArgs ss
	createInfoListToMiddle dvc (U14 ci :** cis) = (:**)
		<$> (U11 <$> createInfoToMiddle dvc ci)
		<*> createInfoListToMiddle dvc cis

createInfoToMiddle :: (
	ShaderStage.CreateInfoListToMiddle ssas,
	VertexInputState.BindingStrideList vibs VertexInput.Rate,
	VertexInputState.AttributeDescriptions vibs vias ) =>
	Device.D sd ->
	CreateInfo n ssas '(nvis, vibs, vias)
		iasa tsssa vpsa rstsa mssa dssa cbsa dsa sl rpa '(sb, vibs', vias', slbtss') ->
	IO (M.CreateInfo n (ShaderStage.MiddleArgs ssas)
		nvis iasa tsssa vpsa rstsa mssa dssa cbsa dsa)
createInfoToMiddle dvc CreateInfo {
	createInfoNext = mnxt,
	createInfoFlags = flgs,
	createInfoStages = stgs,
	createInfoVertexInputState = vis,
	createInfoInputAssemblyState = ias,
	createInfoTessellationState = ts,
	createInfoViewportState = vs,
	createInfoRasterizationState = rs,
	createInfoMultisampleState = ms,
	createInfoDepthStencilState = dss,
	createInfoColorBlendState = cbs,
	createInfoDynamicState = ds,
	createInfoLayout = U3 (Layout.P lyt),
	createInfoRenderPass = RenderPass.R rp,
	createInfoSubpass = sp,
	createInfoBasePipelineHandle = bph,
	createInfoBasePipelineIndex = bpi } = do
	stgs' <- ShaderStage.createInfoListToMiddle dvc stgs
	bph' <- maybe M.gNull (\(U4 (G g)) -> pure g) bph
	pure M.CreateInfo {
		M.createInfoNext = mnxt,
		M.createInfoFlags = flgs,
		M.createInfoStages = stgs',
		M.createInfoVertexInputState =
			VertexInputState.createInfoToMiddle . unU3 <$> vis,
		M.createInfoInputAssemblyState = ias,
		M.createInfoTessellationState = ts,
		M.createInfoViewportState = vs,
		M.createInfoRasterizationState = rs,
		M.createInfoMultisampleState = ms,
		M.createInfoDepthStencilState = dss,
		M.createInfoColorBlendState = cbs,
		M.createInfoDynamicState = ds,
		M.createInfoLayout = lyt,
		M.createInfoRenderPass = rp,
		M.createInfoSubpass = sp,
		M.createInfoBasePipelineHandle = bph',
		M.createInfoBasePipelineIndex = bpi }

-- DESTROY SHADER STAGES

class DestroyShaderStages mvs macs where
	destroyShaderStages' :: Device.D sd ->
		HeteroParList.PL (U11 M.CreateInfo) mvs ->
		HeteroParList.PL2
			(TPMaybe.M (U2 AllocationCallbacks.A)) macs -> IO ()

instance DestroyShaderStages '[] '[] where
	destroyShaderStages' _ HeteroParList.Nil HeteroParList.Nil = pure ()

instance (ShaderStage.DestroyShaderModuleList a2 mac, DestroyShaderStages ss macs) =>
	DestroyShaderStages ('(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) ': ss) (mac ': macs) where
	destroyShaderStages' dvc (U11 cim :** cims) (mac :** macs) = do
		ShaderStage.destroyShaderModuleList dvc (M.createInfoStages cim) mac
		destroyShaderStages' dvc cims macs

class AllocationCallbacksListListFromCreateInfoList cias where
	type TAllocationCallbacksListList cias :: [[Maybe (Type, Type)]]
	allocationCallbacksListListFromCreateInfoList ::
		HeteroParList.PL (U14 CreateInfo) cias ->
		HeteroParList.PL2
			(TPMaybe.M (U2 AllocationCallbacks.A))
			(TAllocationCallbacksListList cias)

instance AllocationCallbacksListListFromCreateInfoList '[] where
	type TAllocationCallbacksListList '[] = '[]
	allocationCallbacksListListFromCreateInfoList
		HeteroParList.Nil = HeteroParList.Nil

instance (
	HeteroParList.Map3_5 (TIndex.I1_14 cia),
	AllocationCallbacksListListFromCreateInfoList cias ) =>
	AllocationCallbacksListListFromCreateInfoList (cia ': cias) where
	type TAllocationCallbacksListList (cia ': cias) =
		TMapIndex.M3_5 (TIndex.I1_14 cia) ':
		TAllocationCallbacksListList cias
	allocationCallbacksListListFromCreateInfoList (U14 ci :** cis) =
		allocationCallbacksListFromCreateInfo ci :**
		allocationCallbacksListListFromCreateInfoList cis

allocationCallbacksListFromCreateInfo :: HeteroParList.Map3_5 ssas =>
	CreateInfo mn ssas visa iasa tsssa vpsa rstsa mssa dssa cbsa dsa lyta rpa bpha ->
	HeteroParList.PL (TPMaybe.M (U2 AllocationCallbacks.A)) (TMapIndex.M3_5 ssas)
allocationCallbacksListFromCreateInfo =
	ShaderStage.allocationCallbacksListFromCreateInfoList . createInfoStages

-- Group

data Group sd ma sg k gas = Group (Device.D sd)
	(TPMaybe.M (U2 AllocationCallbacks.A) ma)
	TSem (TVar (Map.Map k ( HeteroParList.PL (U3 (G sg)) gas)))

group :: AllocationCallbacks.ToMiddle ma =>
	Device.D sd -> TPMaybe.M (U2 AllocationCallbacks.A) ma ->
	(forall sg . Group sd ma sg k gas -> 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
	((\gs -> M.destroyGs mdvc (gListToMiddle gs) mmac) `mapM_`)
		=<< atomically (readTVar m)
	pure rtn

createGs' :: (
	Ord k,
	CreateInfoListToMiddle cias, AllocationCallbacks.ToMiddle mac ) =>
	Group sd mac sg k (CreateInfoListArgsToGArgs cias) -> k ->
	Maybe (Cache.P sc) ->
	HeteroParList.PL (U14 CreateInfo) cias ->
	IO (Either String
		(HeteroParList.PL (U3 (G sg)) (CreateInfoListArgsToGArgs cias)))
createGs' (Group d@(Device.D dvc)
	(AllocationCallbacks.toMiddle -> mmac) sem gss) k
	((Cache.pToMiddle <$>) -> mc) cis = do
	ok <- atomically do
		mx <- Map.lookup k <$> readTVar gss
		case mx of
			Nothing -> waitTSem sem >> pure True
			Just _ -> pure False
	if ok
	then do	gs <- createInfoListToMiddle d cis >>= \cis' -> M.createGs dvc mc cis' mmac
				<* destroyShaderStages' d cis'
					(allocationCallbacksListListFromCreateInfoList cis)
		let	gs' = gListFromMiddle gs
		atomically $ modifyTVar gss (Map.insert k gs') >> signalTSem sem
		pure $ Right gs'
	else pure . Left $
		"Gpu.Vulkan.Pipeline.Graphics.create' :: The key already exist"

unsafeDestroyGs :: (Ord k, AllocationCallbacks.ToMiddle ma) =>
	Group sd ma sg k gas -> k -> IO (Either String ())
unsafeDestroyGs (Group (Device.D mdvc)
	(AllocationCallbacks.toMiddle -> mma) sem gss) k = do
	mgs <- atomically do
		mx <- Map.lookup k <$> readTVar gss
		case mx of
			Nothing -> pure Nothing
			Just _ -> waitTSem sem >> pure mx
	case mgs of
		Nothing -> pure $ Left
			"Gpu.Vulkan.Pipeline.Graphics.unsafeDestroyGs: No such key"
		Just gs -> do
			atomically . modifyTVar gss $ Map.delete k
			M.destroyGs mdvc (gListToMiddle gs) mma
			Right <$> atomically (signalTSem sem)

lookup :: Ord k =>
	Group sd ma sg k gas -> k -> IO (Maybe (HeteroParList.PL (U3 (G sg)) gas))
lookup (Group _ _ _sem gss) k = atomically $ Map.lookup k <$> readTVar gss