packages feed

wgpu-hs-0.3.0.0: src-internal/WGPU/Internal/Adapter.hs

{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}

-- |
-- Module      : WGPU.Internal.Adapter
-- Description : Adapter (physical device).
module WGPU.Internal.Adapter
  ( -- * Types
    Adapter (..),
    AdapterType (..),
    BackendType (..),
    AdapterProperties (..),

    -- * Functions
    requestAdapter,
    getAdapterProperties,
    adapterPropertiesToText,
  )
where

import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Word (Word32)
import Foreign (nullPtr)
import Foreign.Ptr (Ptr)
import Text.Printf (printf)
import WGPU.Internal.ChainedStruct (ChainedStruct (EmptyChain))
import WGPU.Internal.Instance (Instance, wgpuHsInstance)
import WGPU.Internal.Memory
  ( FromRaw,
    ToRaw,
    allocaC,
    evalContT,
    freeHaskellFunPtr,
    fromRaw,
    fromRawPtr,
    newEmptyMVar,
    putMVar,
    raw,
    rawPtr,
    showWithPtr,
    takeMVar,
  )
import WGPU.Internal.Surface (Surface, surfaceInst)
import WGPU.Raw.Generated.Enum.WGPUAdapterType (WGPUAdapterType)
import qualified WGPU.Raw.Generated.Enum.WGPUAdapterType as WGPUAdapterType
import WGPU.Raw.Generated.Enum.WGPUBackendType (WGPUBackendType)
import qualified WGPU.Raw.Generated.Enum.WGPUBackendType as WGPUBackendType
import qualified WGPU.Raw.Generated.Enum.WGPUNativeSType as WGPUSType
import qualified WGPU.Raw.Generated.Fun as RawFun
import WGPU.Raw.Generated.Struct.WGPUAdapterProperties (WGPUAdapterProperties)
import qualified WGPU.Raw.Generated.Struct.WGPUAdapterProperties as WGPUAdapterProperties
import WGPU.Raw.Generated.Struct.WGPURequestAdapterOptions
  ( WGPURequestAdapterOptions,
  )
import qualified WGPU.Raw.Generated.Struct.WGPURequestAdapterOptions as WGPURequestAdapterOptions
import WGPU.Raw.Types
  ( WGPUAdapter (WGPUAdapter),
    WGPUInstance (WGPUInstance),
    WGPURequestAdapterCallback,
  )

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

-- | Handle to a physical graphics and/or compute device.
--
-- Request an 'Adapter' for a 'Surface' using the 'requestAdapter' function.
data Adapter = Adapter
  { adapterInst :: !Instance,
    wgpuAdapter :: !WGPUAdapter
  }

instance Show Adapter where
  show a =
    let Adapter _ (WGPUAdapter ptr) = a
     in showWithPtr "Adapter" ptr

instance Eq Adapter where
  (==) a1 a2 =
    let Adapter _ (WGPUAdapter a1_ptr) = a1
        Adapter _ (WGPUAdapter a2_ptr) = a2
     in a1_ptr == a2_ptr

instance ToRaw Adapter WGPUAdapter where
  raw = pure . wgpuAdapter

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

-- | Physical device type.
data AdapterType
  = AdapterTypeDiscreteGPU
  | AdapterTypeIntegratedGPU
  | AdapterTypeCPU
  | AdapterTypeUnknown
  deriving (Eq, Show)

instance ToRaw AdapterType WGPUAdapterType where
  raw adapterType = case adapterType of
    AdapterTypeDiscreteGPU -> pure WGPUAdapterType.DiscreteGPU
    AdapterTypeIntegratedGPU -> pure WGPUAdapterType.IntegratedGPU
    AdapterTypeCPU -> pure WGPUAdapterType.CPU
    AdapterTypeUnknown -> pure WGPUAdapterType.Unknown

instance FromRaw WGPUAdapterType AdapterType where
  fromRaw wAdapterType = pure $ case wAdapterType of
    WGPUAdapterType.DiscreteGPU -> AdapterTypeDiscreteGPU
    WGPUAdapterType.IntegratedGPU -> AdapterTypeIntegratedGPU
    WGPUAdapterType.CPU -> AdapterTypeCPU
    WGPUAdapterType.Unknown -> AdapterTypeUnknown
    _ -> AdapterTypeUnknown

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

-- | Backends supported by WGPU.
data BackendType
  = BackendTypeNull
  | BackendTypeD3D11
  | BackendTypeD3D12
  | BackendTypeMetal
  | BackendTypeVulkan
  | BackendTypeOpenGL
  | BackendTypeOpenGLES
  deriving (Eq, Show)

instance ToRaw BackendType WGPUBackendType where
  raw backendType = case backendType of
    BackendTypeNull -> pure WGPUBackendType.Null
    BackendTypeD3D11 -> pure WGPUBackendType.D3D11
    BackendTypeD3D12 -> pure WGPUBackendType.D3D12
    BackendTypeMetal -> pure WGPUBackendType.Metal
    BackendTypeVulkan -> pure WGPUBackendType.Vulkan
    BackendTypeOpenGL -> pure WGPUBackendType.OpenGL
    BackendTypeOpenGLES -> pure WGPUBackendType.OpenGLES

instance FromRaw WGPUBackendType BackendType where
  fromRaw wBackendType = pure $ case wBackendType of
    WGPUBackendType.Null -> BackendTypeNull
    WGPUBackendType.D3D11 -> BackendTypeD3D11
    WGPUBackendType.D3D12 -> BackendTypeD3D12
    WGPUBackendType.Metal -> BackendTypeMetal
    WGPUBackendType.Vulkan -> BackendTypeVulkan
    WGPUBackendType.OpenGL -> BackendTypeOpenGL
    WGPUBackendType.OpenGLES -> BackendTypeOpenGLES
    _ -> BackendTypeNull

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

data AdapterProperties = AdapterProperties
  { deviceID :: !Word32,
    vendorID :: !Word32,
    adapterName :: !Text,
    driverDescription :: !Text,
    adapterType :: !AdapterType,
    backendType :: !BackendType
  }
  deriving (Eq, Show)

instance ToRaw AdapterProperties WGPUAdapterProperties where
  raw AdapterProperties {..} = do
    chain_ptr <- rawPtr (EmptyChain WGPUSType.AdapterExtras)
    name_ptr <- rawPtr adapterName
    driverDescription_ptr <- rawPtr driverDescription
    n_adapterType <- raw adapterType
    n_backendType <- raw backendType
    pure
      WGPUAdapterProperties.WGPUAdapterProperties
        { nextInChain = chain_ptr,
          deviceID = deviceID,
          vendorID = vendorID,
          name = name_ptr,
          driverDescription = driverDescription_ptr,
          adapterType = n_adapterType,
          backendType = n_backendType
        }

instance FromRaw WGPUAdapterProperties AdapterProperties where
  fromRaw WGPUAdapterProperties.WGPUAdapterProperties {..} = do
    n_adapterName <- fromRaw name
    n_driverDescription <- fromRaw driverDescription
    n_adapterType <- fromRaw adapterType
    n_backendType <- fromRaw backendType
    pure
      AdapterProperties
        { deviceID = deviceID,
          vendorID = vendorID,
          adapterName = n_adapterName,
          driverDescription = n_driverDescription,
          adapterType = n_adapterType,
          backendType = n_backendType
        }

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

-- | Request an 'Adapter' that is compatible with a given 'Surface'.
--
-- This action blocks until an available adapter is returned.
requestAdapter ::
  (MonadIO m) =>
  -- | Existing surface for which to request an @Adapter@.
  Surface ->
  -- | The returned @Adapter@, if it could be retrieved.
  m (Maybe Adapter)
requestAdapter surface = liftIO . evalContT $ do
  let inst = surfaceInst surface

  adaptmv <- newEmptyMVar
  callback <- mkAdapterCallback (\a _ -> putMVar adaptmv a)

  requestAdapterOptions_ptr <- rawPtr (RequestAdapterOptions surface)
  RawFun.wgpuInstanceRequestAdapter
    (wgpuHsInstance inst)
    (WGPUInstance nullPtr)
    requestAdapterOptions_ptr
    callback
    nullPtr

  adapter <- takeMVar adaptmv
  freeHaskellFunPtr callback

  pure $ case adapter of
    WGPUAdapter ptr | ptr == nullPtr -> Nothing
    WGPUAdapter _ -> Just (Adapter inst adapter)

mkAdapterCallback ::
  MonadIO m =>
  (WGPUAdapter -> Ptr () -> IO ()) ->
  m WGPURequestAdapterCallback
mkAdapterCallback = liftIO . mkAdapterCallbackIO

foreign import ccall "wrapper"
  mkAdapterCallbackIO ::
    (WGPUAdapter -> Ptr () -> IO ()) -> IO WGPURequestAdapterCallback

newtype RequestAdapterOptions = RequestAdapterOptions {compatibleSurface :: Surface}

instance ToRaw RequestAdapterOptions WGPURequestAdapterOptions where
  raw RequestAdapterOptions {..} = do
    n_surface <- raw compatibleSurface
    pure
      WGPURequestAdapterOptions.WGPURequestAdapterOptions
        { nextInChain = nullPtr,
          compatibleSurface = n_surface
        }

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

-- | Get information about an adapter.
getAdapterProperties :: MonadIO m => Adapter -> m AdapterProperties
getAdapterProperties adapter = liftIO $
  evalContT $ do
    wgpuAdapterProperties_ptr <- allocaC
    RawFun.wgpuAdapterGetProperties
      (wgpuHsInstance . adapterInst $ adapter)
      (wgpuAdapter adapter)
      wgpuAdapterProperties_ptr
    fromRawPtr wgpuAdapterProperties_ptr

-- | Format adapter properties into a multi-line block of text.
--
-- This can be useful for debugging purposes.
adapterPropertiesToText :: AdapterProperties -> Text
adapterPropertiesToText AdapterProperties {..} =
  Text.unlines
    [ "Adapter Properties:",
      "  device ID   : " <> Text.pack (printf "0x%08x" deviceID),
      "  vendor ID   : " <> Text.pack (printf "0x%08x" vendorID),
      "  name        : "
        <> if Text.null adapterName
          then "(unknown)"
          else adapterName,
      "  description : "
        <> if Text.null driverDescription
          then "(unknown)"
          else driverDescription,
      "  type        : " <> adapterTypeTxt,
      "  backend     : " <> backendTypeTxt
    ]
  where
    adapterTypeTxt :: Text
    adapterTypeTxt = case adapterType of
      AdapterTypeDiscreteGPU -> "Discrete GPU"
      AdapterTypeIntegratedGPU -> "Integrated GPU"
      AdapterTypeCPU -> "CPU"
      AdapterTypeUnknown -> "(unknown)"

    backendTypeTxt :: Text
    backendTypeTxt = case backendType of
      BackendTypeNull -> "(unknown)"
      BackendTypeD3D11 -> "D3D 11"
      BackendTypeD3D12 -> "D3D 12"
      BackendTypeMetal -> "Metal"
      BackendTypeVulkan -> "Vulkan"
      BackendTypeOpenGL -> "OpenGL"
      BackendTypeOpenGLES -> "OpenGL ES"