mischief-render-0.1.0.0: src/Mischief/Render/Material/Definition.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
module Mischief.Render.Material.Definition where
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.State (MonadState (get), put)
import Data.Data
import Data.Foldable
import Data.Kind (Type)
import Data.Map (Map)
import Data.Map qualified as Map
import Data.Set (Set)
import Data.Set qualified as Set
import Data.Text qualified as T
import Data.Text.Encoding qualified as T
import Foreign (Ptr, nullPtr, with)
import Foreign.C.ConstPtr
import GHC.TypeLits
import Mischief.Assets (Asset (..))
import Mischief.ECS
import Mischief.Math qualified as Math
import Mischief.Render.Core
import Mischief.Render.Texture
import Mischief.Render.TextureFormat
import Mischief.Shader hiding (FragmentOutput, VertexOutput)
import Mischief.Shader.Generation hiding (FragmentOutput)
import Mischief.Shader.Internal.Bindings (Bindable, ErasedBinding (..), ErasedBindingValue, createBindGroup, createBindLayout, dummyBinding)
import Mischief.Shader.Internal.Params
import Mischief.Shader.Internal.State
import Mischief.Shader.Types qualified as ST
import Mischief.WGPU
import Mischief.WGPU.Framework (loadShaderFromBytes)
import Mischief.WGPU.Opaque
import Mischief.WGPU.Types.Enums
import Mischief.WGPU.Types.General
import Unsafe.Coerce (unsafeCoerce)
newtype DefaultVI f = DefaultVI (BuiltIn f "vertex_index" Uint)
deriving (Generic)
deriving anyclass (ShaderParam)
newtype DefaultVO f = DefaultVO
{ pos :: BuiltIn f "position" (Vec 4 ST.Float)
}
deriving (Generic)
deriving anyclass (ShaderParam)
class
( Bindable (B a),
ShaderParam (VI a),
ShaderParam (VO a),
Extension (Extends a),
Typeable a,
Typeable (B a),
Typeable (VI a),
Typeable (VO a),
Typeable (Extends a),
Typeable (IsPhantom a)
) =>
Material (a :: Type)
where
type VI a :: Type -> Type
type VO a :: Type -> Type
type B a :: Type -> Type
type Extends a :: [Type]
type Extends a = '[]
type Extandable a :: Bool
type Extandable a = True
type IsPhantom a :: Bool
type IsPhantom a = False
vertex :: VI a GPU -> Shader a (VO a GPU)
default vertex :: (IsPhantom a ~ True) => VI a GPU -> Shader a (VO a GPU)
vertex = undefined
fragment :: VO a GPU -> Shader a (ST.Vec 4 ST.Float)
default fragment :: (IsPhantom a ~ True) => VO a GPU -> Shader a (ST.Vec 4 ST.Float)
fragment = undefined
fetch :: FetchBindings (B a)
data PhantomMaterial = PhantomMaterial deriving (Component)
data MaterialType where
MaterialType :: forall m. (Material m) => Proxy m -> MaterialType
deriving (Component)
data NoMaterial
-- type StoreData MaterialType = (MaterialType, )
-- storeAsset = undefined
liftExt :: forall a b c. (ExtensionOf b a) => Shader a c -> Shader b c
liftExt (Shader a) = Shader a
class Extension (a :: [Type]) where
collectExtBindings :: Set ErasedBinding
instance {-# OVERLAPS #-} Extension '[] where
collectExtBindings = Set.empty
instance (Extension xs, Material a) => Extension (a : xs) where
collectExtBindings = Set.unions [Set.singleton $ ErasedBinding (MaterialType $ Proxy @a) (Proxy @a) (Proxy @(B a)), collectExtBindings @(Extends a), collectExtBindings @xs]
collectBindings :: forall m. (Material m) => Set ErasedBinding
collectBindings = Set.union (Set.singleton $ ErasedBinding (MaterialType $ Proxy @m) (Proxy @m) (Proxy @(B m))) $ collectExtBindings @(Extends m)
type ExtensionOf a b = (Material a, IsExtensionOf a b ~ True)
type family IsExtensionOf a b where
IsExtensionOf a NoMaterial = True
IsExtensionOf a b = IsExtensionOf' '[a] b
type family IsExtensionOf' (m :: [Type]) (target :: Type) :: Bool where
IsExtensionOf' '[] a = False
IsExtensionOf' (a : xs) a = True
IsExtensionOf' (a : xs) t = Or (IsExtensionOf' (Extends a) t) (IsExtensionOf' xs t)
type family Or a b where
Or False False = False
Or a b = True
-- type family ExtendsMaterialRec (e :: Maybe Type) (target :: Type) :: Bool where
-- ExtendsMaterialRec Nothing t = False
-- ExtendsMaterialRec (Just m) t = IsExtensionOf m t
bindings :: forall a. (Material a) => Shader a (B a GPU)
bindings = do
ShaderState {dict} <- get
(ErasedValue a) <- pure $ unwrap $ Map.lookup (typeRep $ Proxy @a) dict
pure $ unsafeCoerce a
extension :: forall e a. (Material e, ExtensionOf a e) => Shader a (B e GPU)
extension = do
ShaderState {dict} <- get
(ErasedValue a) <- pure $ unwrap $ Map.lookup (typeRep $ Proxy @e) dict
pure $ unsafeCoerce a
convertFrag :: forall m. (VO m GPU -> Shader m (ST.Vec 4 ST.Float)) -> VO m GPU -> Shader m (FragmentOutput GPU)
convertFrag s v = do
s' <- s v
pure $ FragmentOutput s'
data FetchBindings b where
FetchBindings ::
forall x y b.
(Bindable b) =>
{ camera :: CameraEntity -> Query System x,
mesh :: MeshEntity -> Query System y,
result :: x -> y -> b CPU
} ->
FetchBindings b
newtype FragmentOutput f = FragmentOutput
{ fragOut1 :: Location f 0 (ST.Vec 4 ST.Float)
}
deriving stock (Generic)
deriving anyclass (ShaderParam)
blendState :: WGPUBlendState
blendState =
WGPUBlendState
{ color =
WGPUBlendComponent
{ srcFactor = wGPUBlendFactor_SrcAlpha,
dstFactor = wGPUBlendFactor_OneMinusSrcAlpha,
operation = wGPUBlendOperation_Add
},
alpha =
WGPUBlendComponent
{ srcFactor = wGPUBlendFactor_One,
dstFactor = wGPUBlendFactor_OneMinusSrcAlpha,
operation = wGPUBlendOperation_Add
}
}
data DrawCommand bindings = Draw {binds :: [ErasedBindingValue], vertices :: Nat}
drawCommand :: forall m. (Material m) => RenderDevice -> Ptr WGPURenderPassEncoder -> BindLayout -> DrawCommand (B m) -> IO ()
drawCommand device encoder layout (Draw b vert) = do
bindGroup <- createBindGroup device layout b
wgpuRenderPassEncoderSetBindGroup encoder 0 bindGroup 0 (ConstPtr nullPtr)
wgpuRenderPassEncoderDraw encoder (fromIntegral vert) 1 0 0
render :: forall mat. (Material mat) => RenderDevice -> RenderQueue -> RenderPipeline -> RenderOptions -> Texture -> [DrawCommand (B mat)] -> System ()
render (RenderDevice device) (RenderQueue queue) (RenderPipeline pipeline) RenderOptions {clear} (Texture {texture = output}) commands = liftIO $ do
bindLayout <- createBindLayout (Set.toList $ collectBindings @mat) (RenderDevice device)
-- bindGroup <- createBindGroup (RenderDevice device) bindLayout b
-- RenderPipeline pipeline <- createPipeline @mat (RenderDevice device) (toWGPUFormat format)
frame <- wgpuTextureCreateView output (ConstPtr nullPtr)
commandEncoder <- withWGPUString "command encoder" $ \label -> do
let desc = WGPUCommandEncoderDescriptor {nextInChain = nullPtr, label}
with desc $ \desc -> wgpuDeviceCreateCommandEncoder device desc
let (clearValue, loadOp) = case clear of
Clear (Math.V3 a b c) -> (wgpuColor (realToFrac a) (realToFrac b) (realToFrac c) 1, wGPULoadOp_Clear)
DontClear -> (wgpuColor 0 0 0 1, wGPULoadOp_Load)
let attachment =
WGPURenderPassColorAttachment
{ clearValue,
storeOp = wGPUStoreOp_Store,
loadOp,
resolveTarget = nullPtr,
depthSlice = wGPU_DEPTH_SLICE_UNDEFINED,
view = frame,
nextInChain = nullPtr
}
renderPassEncoder <- with attachment $ \attachment -> do
withWGPUString "render pass encoder" $ \label -> do
let desc =
WGPURenderPassDescriptor
{ timestampWrites = ConstPtr nullPtr,
occlusionQuerySet = nullPtr,
depthStencilAttachment = ConstPtr nullPtr,
colorAttachments = ConstPtr attachment,
colorAttachmentCount = 1,
label,
nextInChain = nullPtr
}
with desc $ \desc -> do
wgpuCommandEncoderBeginRenderPass commandEncoder (ConstPtr desc)
when (renderPassEncoder == nullPtr) $ error "Couldn't encode render pass."
wgpuRenderPassEncoderSetPipeline renderPassEncoder pipeline
for_ commands $ drawCommand @mat (RenderDevice device) renderPassEncoder bindLayout
wgpuRenderPassEncoderEnd renderPassEncoder
wgpuRenderPassEncoderRelease renderPassEncoder
commandBuffer <- withWGPUString "command buffer" $ \label -> do
let desc = WGPUCommandBufferDescriptor {label, nextInChain = nullPtr}
with desc $ \desc -> wgpuCommandEncoderFinish commandEncoder (ConstPtr desc)
when (commandBuffer == nullPtr) $ error "Couldn't create command buffer."
with commandBuffer $ \commandBuffer -> do
wgpuQueueSubmit queue 1 (ConstPtr commandBuffer)
wgpuCommandBufferRelease commandBuffer
wgpuCommandEncoderRelease commandEncoder
wgpuTextureViewRelease frame
data Clear = DontClear | Clear (Math.V3 Prelude.Float)
data RenderOptions = RenderOptions
{ clear :: Clear,
nvertices :: Nat
}
instance Default RenderOptions where
def =
RenderOptions
{ clear = DontClear,
nvertices = 6
}