packages feed

gpu-vulkan-0.1.0.155: src/Gpu/Vulkan/Object.hs

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

module Gpu.Vulkan.Object (

	-- * OBJECT

	O(..),

	-- ** Synonyms

	Static, Dynamic,

	-- *** static

	Atom, List, Image,
	AtomNoName, ListNoName, ImageNoName,
	AtomMaybeName, ListMaybeName, ImageMaybeName,

	-- *** dynamic

	DynAtomNew, DynList, DynImage,
	DynAtomNoName, DynListNoName, DynImageNoName,
	DynAtomMaybeName, DynListMaybeName, DynImageMaybeName,

	-- ** Type Of Object

	TypeOf,

	-- * OBJECT LENGTH

	Length,

	-- ** Synonyms

	pattern LengthAtom,
	pattern LengthList,
	pattern LengthImage,
	lengthImageRow, lengthImageWidth, lengthImageHeight, lengthImageDepth,
	lengthImageLayerCount,

	pattern LengthDynAtom,
	pattern LengthDynList,
	pattern LengthDynImage,

	pattern LengthList',

	-- ** Find Length

	LengthOf(..),

	-- * ONLY DYNAMIC LENGTHS

	OnlyDynamicLengths(..),

	-- * STORE

	Store(..),

	-- * SIZE, ALIGNMENT AND OFFSET

	-- ** Whole Size

	wholeSize, WholeAlign(..),

	-- ** Offset Range

	offsetRange, offsetSize, OffsetRange,

	-- ** Offset Of List

	offsetOfList, OffsetOfList(..),

	-- ** Size AlignmentList

	SizeAlignmentList(..), SizeAlignment(..),

	) where

import Prelude hiding (length)

import GHC.TypeLits (Symbol)
import GHC.TypeNats
import Foreign.Ptr
import Data.Proxy
import Data.HeteroParList qualified as HeteroParList
import Data.HeteroParList (pattern (:**))

import Data.Maybe
import Data.Word

import Foreign.Storable (Storable)
import Gpu.Vulkan.Object.Base qualified as K
import Gpu.Vulkan.Device.Middle.Internal qualified as Device.M

-- OBJECT

data O = Static_ K.O | Dynamic Nat K.O

type Static algn mnm ot v = 'Static_ ('K.O algn mnm ot v)
type Dynamic n algn mnm ot v = 'Dynamic n ('K.O algn mnm ot v)

type Atom algn v nm = AtomMaybeName algn v ('Just nm)
type List algn v nm = ListMaybeName algn v ('Just nm)
type Image algn v nm = ImageMaybeName algn v ('Just nm)

type AtomNoName algn v = AtomMaybeName algn v 'Nothing
type ListNoName algn v = ListMaybeName algn v 'Nothing
type ImageNoName algn v = ImageMaybeName algn v 'Nothing

type AtomMaybeName algn v mnm = Static_ (K.AtomMaybeName algn v mnm)
type ListMaybeName algn v mnm = Static_ (K.ListMaybeName algn v mnm)
type ImageMaybeName algn v mnm = Static_ (K.ImageMaybeName algn v mnm)

type DynAtomNew n algn v nm = 'Dynamic n (K.AtomNew algn v nm)
type DynList n algn v nm = 'Dynamic n (K.List algn v nm)
type DynImage n algn v nm = 'Dynamic n (K.Image algn v nm)

type DynAtomNoName n algn v = 'Dynamic n (K.AtomNoName algn v)
type DynListNoName n algn v = 'Dynamic n (K.ListNoName algn v)
type DynImageNoName n algn v = 'Dynamic n (K.ImageNoName algn v)

type DynAtomMaybeName n algn v mnm = 'Dynamic n (K.AtomMaybeName algn v mnm)
type DynListMaybeName n algn v mnm = 'Dynamic n (K.ListMaybeName algn v mnm)
type DynImageMaybeName n algn v mnm = 'Dynamic n (K.ImageMaybeName algn v mnm)

type family TypeOf obj where
	TypeOf (Static_ kobj) = K.TypeOf kobj
	TypeOf ('Dynamic n kobj) = K.TypeOf kobj

-- OBJECT LENGTH

data Length obj where
	LengthStatic :: K.Length kobj -> Length ('Static_ kobj)
	LengthDynamic ::
		K.Length kobj -> Length ('Dynamic n kobj)

deriving instance Eq (Length obj)
deriving instance Show (Length obj)

pattern LengthAtom :: Length ('Static_ (K.AtomMaybeName algn v mnm))
pattern LengthAtom <- LengthStatic K.LengthAtom where
	LengthAtom = LengthStatic K.LengthAtom

{-# COMPLETE LengthList #-}
{-# COMPLETE LengthList' #-}

pattern LengthList :: Device.M.Size ->
	Length ('Static_ (K.ListMaybeName algn v mnm))
pattern LengthList n <- LengthStatic (K.LengthList n) where
	LengthList n = LengthStatic (K.LengthList n)

pattern LengthList' :: Word64 -> Length ('Static_ (K.ListMaybeName algn v nm))
pattern LengthList' n <- LengthStatic (K.LengthList (Device.M.Size n)) where
	LengthList' n = LengthStatic (K.LengthList (Device.M.Size n))

{-# COMPLETE LengthImage #-}

pattern LengthImage ::
	Device.M.Size -> Device.M.Size -> Device.M.Size -> Device.M.Size -> Device.M.Size ->
	Length ('Static_ (K.ImageMaybeName algn v mnm))
pattern LengthImage {
	lengthImageRow, lengthImageWidth,
	lengthImageHeight, lengthImageDepth, lengthImageLayerCount } = LengthStatic (K.LengthImage
		lengthImageRow lengthImageWidth
		lengthImageHeight lengthImageDepth lengthImageLayerCount)

pattern LengthDynAtom :: Length ('Dynamic n (K.Atom algn v nm))
pattern LengthDynAtom <- LengthDynamic K.LengthAtom where
	LengthDynAtom = LengthDynamic K.LengthAtom

pattern LengthDynList :: Device.M.Size -> Length ('Dynamic n (K.List algn v nm))
pattern LengthDynList n <- LengthDynamic (K.LengthList n) where
	LengthDynList n = LengthDynamic (K.LengthList n)

pattern LengthDynImage ::
	Device.M.Size -> Device.M.Size -> Device.M.Size -> Device.M.Size -> Device.M.Size -> Length ('Dynamic n (K.Image algn v nm))
pattern LengthDynImage kr kw kh kd klc <- (LengthDynamic (K.LengthImage kr kw kh kd klc))
	where LengthDynImage kr kw kh kd klc = LengthDynamic (K.LengthImage kr kw kh kd klc)

class LengthOf (obj :: O) (objs :: [O]) where
	lengthOf :: HeteroParList.PL Length objs -> Length obj

instance LengthOf obj (obj ': objs) where lengthOf (ln :** _) = ln

instance {-# OVERLAPPABLE #-} LengthOf obj objs =>
	LengthOf obj (obj' ': objs) where
	lengthOf (_ :** lns) = lengthOf @obj lns

-- ONLY DYNAMIC LENGTH

class OnlyDynamicLengths (objs :: [O]) where
	type OnlyDynamics objs :: [K.O]
	onlyDynamicLength ::
		HeteroParList.PL Length objs ->
		HeteroParList.PL K.Length (OnlyDynamics objs)

instance OnlyDynamicLengths '[] where
	type OnlyDynamics '[] = '[]
	onlyDynamicLength HeteroParList.Nil = HeteroParList.Nil

instance OnlyDynamicLengths objs => OnlyDynamicLengths ('Static_ _o ': objs) where
	type OnlyDynamics ('Static_ _o ': objs) = OnlyDynamics objs
	onlyDynamicLength (LengthStatic _ :** lns) = onlyDynamicLength lns

instance OnlyDynamicLengths objs => OnlyDynamicLengths ('Dynamic _n o ': objs) where
	type OnlyDynamics ('Dynamic _n o ': objs) = o ': OnlyDynamics objs
	onlyDynamicLength (LengthDynamic ln :** lns) =
		ln :** onlyDynamicLength lns

-- STORE

class Store v obj where
	store :: Ptr (TypeOf obj) -> Length obj -> v -> IO ()
	load :: Ptr (TypeOf obj) -> Length obj -> IO v
	length :: v -> Length obj

instance K.Store v bobj => Store v (Static_ bobj) where
	store p (LengthStatic kln) = K.store p kln
	load p (LengthStatic kln) = K.load p kln
	length = LengthStatic . K.length

instance (KnownNat n, K.Store v bobj) => Store [Maybe v] ('Dynamic n bobj) where
	store p0 (LengthDynamic kln) =
		go p0 (natVal (Proxy :: Proxy n))
		where
		go _ _ [] = pure ()
		go _ n _ | n < 1 = pure ()
		go p n (Just x : xs) = do
			K.store p kln x >> go (nextObject p kln) (n - 1) xs
		go p n (Nothing : xs) = do
			go (nextObject p kln) (n - 1) xs
	load p0 (LengthDynamic kln) = go p0 (natVal (Proxy :: Proxy n))
		where
		go _ n | n < 1 = pure []
		go p n = (:) <$> (Just <$> K.load p kln) <*> go (nextObject p kln) (n - 1)
	length = LengthDynamic . K.length . fromJust . head

nextObject :: forall kobj . K.SizeAlignment kobj =>
	Ptr (K.TypeOf kobj) -> K.Length kobj -> Ptr (K.TypeOf kobj)
nextObject p ln = p `plusPtr` fromIntegral n
	where
	n = ((K.size ln - 1) `div` algn + 1) * algn
	algn = K.alignment @kobj

-- SIZE, ALIGNMENT AND OFFSET

-- WholeSize

wholeSize :: SizeAlignmentList objs =>
	HeteroParList.PL Length objs -> Size
wholeSize = wholeSizeFromSzAlgns 0 . sizeAlignmentList

wholeSizeFromSzAlgns ::
	Size -> HeteroParList.PL SizeAlignmentOf objs -> Size
wholeSizeFromSzAlgns sz0 HeteroParList.Nil = sz0
wholeSizeFromSzAlgns sz0 (SizeAlignmentOf dn sz algn :** saoo) =
	wholeSizeFromSzAlgns (adjust algn sz0 + dn * sz) saoo

class WholeAlign (objs :: [O]) where wholeAlign :: Size

instance WholeAlign '[] where wholeAlign = 1

instance (SizeAlignment obj, WholeAlign objs) =>
	WholeAlign (obj ': objs) where
	wholeAlign = alignment @obj `lcm` wholeAlign @objs

-- OffsetRange

offsetRange :: forall obj objs i . OffsetRange obj objs i => Device.M.Size ->
	HeteroParList.PL Length objs -> (Device.M.Size, Device.M.Size)
offsetRange ost0 lns = offsetRangeFromSzAlgns' @obj @_ @i ost0 $ sizeAlignmentList lns

offsetSize :: forall obj objs i . OffsetRange obj objs i => Device.M.Size ->
	HeteroParList.PL Length objs -> (Device.M.Size, Device.M.Size)
offsetSize ost0 lns = offsetSizeFromSzAlgns' @obj @_ @i ost0 $ sizeAlignmentList lns

class (SizeAlignmentList vs, HeteroParList.TypeIndex v vs) =>
	OffsetRange (v :: O) (vs :: [O]) (i :: Nat) where
	offsetRangeFromSzAlgns' ::
		Device.M.Size -> HeteroParList.PL SizeAlignmentOf vs ->
		(Device.M.Size, Device.M.Size)
	offsetSizeFromSzAlgns' ::
		Device.M.Size -> HeteroParList.PL SizeAlignmentOf vs ->
		(Device.M.Size, Device.M.Size)

instance (SizeAlignment v, SizeAlignmentList vs) =>
	OffsetRange v (v ': vs) 0 where
	offsetRangeFromSzAlgns' ost (SizeAlignmentOf _dn sz algn :** _) =
		(adjust algn ost, sz)
	offsetSizeFromSzAlgns' ost (SizeAlignmentOf dn sz algn :** _) =
		(adjust algn ost, dn * sz)

instance {-# OVERLAPPABLE #-}
	(SizeAlignment v, OffsetRange v vs (i - 1)) =>
	OffsetRange v (v ': vs) i where
	offsetRangeFromSzAlgns' ost (SizeAlignmentOf dn sz algn :** sas) =
		offsetRangeFromSzAlgns' @v @vs @(i - 1) (adjust algn ost + dn * sz) sas
	offsetSizeFromSzAlgns' ost (SizeAlignmentOf dn sz algn :** sas) =
		offsetSizeFromSzAlgns' @v @vs @(i - 1) (adjust algn ost + dn * sz) sas

instance {-# OVERLAPPABLE #-} (SizeAlignment v', OffsetRange v vs i) =>
	OffsetRange v (v' ': vs) i where
	offsetRangeFromSzAlgns' ost (SizeAlignmentOf dn sz algn :** sas) =
		offsetRangeFromSzAlgns' @v @vs @i (adjust algn ost + dn * sz) sas
	offsetSizeFromSzAlgns' ost (SizeAlignmentOf dn sz algn :** sas) =
		offsetSizeFromSzAlgns' @v @vs @i (adjust algn ost + dn * sz) sas

-- OffsetOfList

offsetOfList :: forall v onm vs . OffsetOfList v onm vs =>
	HeteroParList.PL Length vs -> (Device.M.Size, Device.M.Size)
offsetOfList = offsetRangeListFromSzAlgns @v @onm 0 . sizeAlignmentList

class SizeAlignmentList objs =>
	OffsetOfList v (nm :: Symbol) (objs :: [O]) where
	offsetRangeListFromSzAlgns ::
		Device.M.Size -> HeteroParList.PL SizeAlignmentOf objs ->
		(Device.M.Size, Device.M.Size)

instance (Storable v, KnownNat oalgn, SizeAlignmentList objs) =>
	OffsetOfList v nm (List oalgn v nm ': objs) where
	offsetRangeListFromSzAlgns ost (SizeAlignmentOf _ sz algn :** _) =
		(adjust algn ost, sz)

instance {-# OVERLAPPABLE #-} (SizeAlignment obj, OffsetOfList v nm objs) =>
	OffsetOfList v nm (obj ': objs) where
	offsetRangeListFromSzAlgns ost (SizeAlignmentOf dn sz algn :** sas) =
		offsetRangeListFromSzAlgns
			@v @nm @objs (adjust algn ost + dn * sz) sas

adjust :: Device.M.Size -> Device.M.Size -> Device.M.Size
adjust algn ost = ((ost - 1) `div` algn + 1) * algn

-- SizeAlignmentList

data SizeAlignmentOf (obj :: O) = SizeAlignmentOf DynNum Size ObjAlignment
	deriving Show

type DynNum = Device.M.Size
type Size = Device.M.Size
type ObjAlignment = Device.M.Size

class SizeAlignmentList objs where
	sizeAlignmentListWholeAlignment :: ObjAlignment
	sizeAlignmentList :: HeteroParList.PL Length objs ->
		HeteroParList.PL SizeAlignmentOf objs

instance SizeAlignmentList '[] where
	sizeAlignmentListWholeAlignment = 1
	sizeAlignmentList HeteroParList.Nil = HeteroParList.Nil

instance (SizeAlignment obj, SizeAlignmentList objs) =>
	SizeAlignmentList (obj ': objs) where
	sizeAlignmentListWholeAlignment =
		alignment @obj `lcm` sizeAlignmentListWholeAlignment @objs
	sizeAlignmentList (ln :** lns) =
		SizeAlignmentOf
			(dynNum @obj) (size ln) (alignment @obj) :**
		sizeAlignmentList lns

-- SizeAlignment

class SizeAlignment obj where
	dynNum :: Device.M.Size
	size :: Length obj -> Device.M.Size
	alignment :: Device.M.Size

instance K.SizeAlignment kobj => SizeAlignment (Static_ kobj) where
	dynNum = 1
	size (LengthStatic kln) = K.size kln
	alignment = K.alignment @kobj

instance (KnownNat n, K.SizeAlignment kobj) =>
	SizeAlignment ('Dynamic n kobj) where
	dynNum = fromIntegral $ natVal (Proxy :: Proxy n)
	size (LengthDynamic kln) = K.size kln
	alignment = K.alignment @kobj