packages feed

wgpu-hs-0.1.0.0: src-internal/WGPU/Internal/Shader.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}

-- |
-- Module      : WGPU.Internal.Shader
-- Description : Shader modules.
module WGPU.Internal.Shader
  ( -- * Types
    ShaderModule,
    ShaderModuleDescriptor (..),
    ShaderSource (..),
    SPIRV (..),
    WGSL (..),
    ShaderEntryPoint (..),

    -- * Functions
    createShaderModule,
    createShaderModuleSPIRV,
    createShaderModuleWGSL,
  )
where

import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Cont (evalContT)
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import Data.String (IsString)
import Data.Text (Text)
import Data.Word (Word32, Word8)
import Foreign (Ptr, castPtr, sizeOf)
import Foreign.C (CChar)
import WGPU.Internal.ChainedStruct (ChainedStruct (EmptyChain, PtrChain))
import WGPU.Internal.Device (Device, deviceInst, wgpuDevice)
import WGPU.Internal.Instance (Instance, wgpuHsInstance)
import WGPU.Internal.Memory (ToRaw, ToRawPtr, raw, rawPtr, showWithPtr)
import qualified WGPU.Raw.Generated.Enum.WGPUSType as WGPUSType
import qualified WGPU.Raw.Generated.Fun as RawFun
import WGPU.Raw.Generated.Struct.WGPUShaderModuleDescriptor (WGPUShaderModuleDescriptor)
import qualified WGPU.Raw.Generated.Struct.WGPUShaderModuleDescriptor as WGPUShaderModuleDescriptor
import WGPU.Raw.Generated.Struct.WGPUShaderModuleSPIRVDescriptor (WGPUShaderModuleSPIRVDescriptor)
import qualified WGPU.Raw.Generated.Struct.WGPUShaderModuleSPIRVDescriptor as WGPUShaderModuleSPIRVDescriptor
import WGPU.Raw.Generated.Struct.WGPUShaderModuleWGSLDescriptor (WGPUShaderModuleWGSLDescriptor)
import qualified WGPU.Raw.Generated.Struct.WGPUShaderModuleWGSLDescriptor as WGPUShaderModuleWGSLDescriptor
import WGPU.Raw.Types (WGPUShaderModule (WGPUShaderModule))

-------------------------------------------------------------------------------

-- | Handle to a compiled shader module.
newtype ShaderModule = ShaderModule {wgpuShaderModule :: WGPUShaderModule}

instance Show ShaderModule where
  show m =
    let ShaderModule (WGPUShaderModule ptr) = m
     in showWithPtr "ShaderModule" ptr

instance Eq ShaderModule where
  (==) m1 m2 =
    let ShaderModule (WGPUShaderModule m1_ptr) = m1
        ShaderModule (WGPUShaderModule m2_ptr) = m2
     in m1_ptr == m2_ptr

instance ToRaw ShaderModule WGPUShaderModule where
  raw = pure . wgpuShaderModule

-------------------------------------------------------------------------------

-- | Create a shader module from either SPIR-V or WGSL source code.
createShaderModule ::
  -- | Device for the shader.
  Device ->
  -- | Descriptor of the shader module.
  ShaderModuleDescriptor ->
  -- | IO action producing the shader module.
  IO ShaderModule
createShaderModule device smd = evalContT $ do
  let inst :: Instance
      inst = deviceInst device
  shaderModuleDescriptor_ptr <- rawPtr smd
  rawShaderModule <-
    liftIO $
      RawFun.wgpuDeviceCreateShaderModule
        (wgpuHsInstance inst)
        (wgpuDevice device)
        shaderModuleDescriptor_ptr
  pure (ShaderModule rawShaderModule)

-- | Create a shader module from SPIR-V source code.
createShaderModuleSPIRV ::
  -- | Device for which the shader should be created.
  Device ->
  -- | Debugging label for the shader.
  Text ->
  -- | Shader source code (SPIR-V bytestring).
  SPIRV ->
  -- | IO action creating the shader module.
  IO ShaderModule
createShaderModuleSPIRV device label spirv =
  createShaderModule device smd
  where
    smd :: ShaderModuleDescriptor
    smd =
      ShaderModuleDescriptor
        { shaderLabel = label,
          source = ShaderSourceSPIRV spirv
        }

-- | Create a shader module from WGSL source code.
createShaderModuleWGSL ::
  -- | Device for which the shader should be created.
  Device ->
  -- | Debugging label for the shader.
  Text ->
  -- | Shader source code (WGSL source string).
  WGSL ->
  -- | IO action creating the shader module.
  IO ShaderModule
createShaderModuleWGSL device label wgsl =
  createShaderModule device smd
  where
    smd :: ShaderModuleDescriptor
    smd =
      ShaderModuleDescriptor
        { shaderLabel = label,
          source = ShaderSourceWGSL wgsl
        }

-------------------------------------------------------------------------------

-- | Descriptor for a shader module.
data ShaderModuleDescriptor = ShaderModuleDescriptor
  { -- | Debug label of the shader module.
    shaderLabel :: !Text,
    -- | Source code for the shader.
    source :: !ShaderSource
  }
  deriving (Eq, Show)

instance ToRaw ShaderModuleDescriptor WGPUShaderModuleDescriptor where
  raw ShaderModuleDescriptor {..} = do
    nextInChain_ptr <-
      case source of
        ShaderSourceSPIRV spirv -> do
          ptr <- rawPtr spirv
          rawPtr (PtrChain WGPUSType.ShaderModuleSPIRVDescriptor ptr)
        ShaderSourceWGSL wgsl -> do
          ptr <- rawPtr wgsl
          rawPtr (PtrChain WGPUSType.ShaderModuleWGSLDescriptor ptr)
    label_ptr <- rawPtr shaderLabel
    pure
      WGPUShaderModuleDescriptor.WGPUShaderModuleDescriptor
        { nextInChain = nextInChain_ptr,
          label = label_ptr
        }

-------------------------------------------------------------------------------

-- | Source for a shader module.
data ShaderSource
  = -- | Use shader source from a SPIRV module (pre-compiled).
    ShaderSourceSPIRV !SPIRV
  | -- | Use shader source from WGSL string.
    ShaderSourceWGSL !WGSL
  deriving (Eq, Show)

-- | Pre-compiled SPIRV module bytes.
newtype SPIRV = SPIRV ByteString deriving (Eq, Show)

instance ToRaw SPIRV WGPUShaderModuleSPIRVDescriptor where
  raw (SPIRV bs) =
    WGPUShaderModuleSPIRVDescriptor.WGPUShaderModuleSPIRVDescriptor
      <$> raw (EmptyChain WGPUSType.ShaderModuleSPIRVDescriptor)
      <*> pure
        ( fromIntegral $
            ByteString.length bs `div` sizeOf (undefined :: Word32)
        )
      <*> ((castPtr :: Ptr Word8 -> Ptr Word32) <$> rawPtr bs)

-- | WGSL shader source code.
newtype WGSL = WGSL Text deriving (Eq, Show)

instance ToRaw WGSL WGPUShaderModuleWGSLDescriptor where
  raw (WGSL txt) =
    WGPUShaderModuleWGSLDescriptor.WGPUShaderModuleWGSLDescriptor
      <$> raw (EmptyChain WGPUSType.ShaderModuleWGSLDescriptor)
      <*> rawPtr txt

-------------------------------------------------------------------------------

-- | Name of a shader entry point.
newtype ShaderEntryPoint = ShaderEntryPoint {unShaderEntryPoint :: Text}
  deriving (Eq, Show, IsString)

instance ToRawPtr ShaderEntryPoint CChar where
  rawPtr = rawPtr . unShaderEntryPoint