packages feed

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

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

-- |
-- Module      : WGPU.Internal.RenderPass
-- Description : Render passes.
module WGPU.Internal.RenderPass
  ( -- * Types
    RenderPipeline (..),
    RenderPassEncoder,
    LoadOp (..),
    StoreOp (..),
    Operations (..),
    RenderPassColorAttachment (..),
    RenderPassDepthStencilAttachment (..),
    RenderPassDescriptor (..),
    Range (..),

    -- * Functions
    beginRenderPass,
    renderPassSetPipeline,
    renderPassDraw,
    endRenderPass,
  )
where

import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Text (Text)
import Data.Vector (Vector)
import Data.Word (Word32)
import Foreign (nullPtr)
import Foreign.C (CBool (CBool), CFloat (CFloat))
import WGPU.Internal.Color (Color, transparentBlack)
import WGPU.Internal.CommandEncoder
  ( CommandEncoder,
    commandEncoderInst,
    wgpuCommandEncoder,
  )
import WGPU.Internal.Instance (Instance, wgpuHsInstance)
import WGPU.Internal.Memory
  ( ToRaw,
    evalContT,
    raw,
    rawArrayPtr,
    rawPtr,
    showWithPtr,
  )
import WGPU.Internal.SMaybe (SMaybe (SJust, SNothing))
import WGPU.Internal.Texture (TextureView)
import qualified WGPU.Raw.Generated.Enum.WGPULoadOp as WGPULoadOp
import WGPU.Raw.Generated.Enum.WGPUStoreOp (WGPUStoreOp)
import qualified WGPU.Raw.Generated.Enum.WGPUStoreOp as WGPUStoreOp
import qualified WGPU.Raw.Generated.Fun as RawFun
import WGPU.Raw.Generated.Struct.WGPURenderPassColorAttachment (WGPURenderPassColorAttachment)
import qualified WGPU.Raw.Generated.Struct.WGPURenderPassColorAttachment as WGPURenderPassColorAttachment
import WGPU.Raw.Generated.Struct.WGPURenderPassDepthStencilAttachment (WGPURenderPassDepthStencilAttachment)
import qualified WGPU.Raw.Generated.Struct.WGPURenderPassDepthStencilAttachment as WGPURenderPassDepthStencilAttachment
import WGPU.Raw.Generated.Struct.WGPURenderPassDescriptor (WGPURenderPassDescriptor)
import qualified WGPU.Raw.Generated.Struct.WGPURenderPassDescriptor as WGPURenderPassDescriptor
import WGPU.Raw.Types
  ( WGPUQuerySet (WGPUQuerySet),
    WGPURenderPassEncoder (WGPURenderPassEncoder),
    WGPURenderPipeline (WGPURenderPipeline),
    WGPUTextureView (WGPUTextureView),
  )

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

newtype RenderPipeline = RenderPipeline {wgpuRenderPipeline :: WGPURenderPipeline}

instance Show RenderPipeline where
  show p =
    let RenderPipeline (WGPURenderPipeline ptr) = p
     in showWithPtr "RenderPipeline" ptr

instance Eq RenderPipeline where
  (==) p1 p2 =
    let RenderPipeline (WGPURenderPipeline p1_ptr) = p1
        RenderPipeline (WGPURenderPipeline p2_ptr) = p2
     in p1_ptr == p2_ptr

instance ToRaw RenderPipeline WGPURenderPipeline where
  raw = pure . wgpuRenderPipeline

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

data RenderPassEncoder = RenderPassEncoder
  { renderPassEncoderInst :: !Instance,
    wgpuRenderPassEncoder :: !WGPURenderPassEncoder
  }

instance Show RenderPassEncoder where
  show e =
    let RenderPassEncoder _ (WGPURenderPassEncoder ptr) = e
     in showWithPtr "RenderPassEncoder" ptr

instance Eq RenderPassEncoder where
  (==) e1 e2 =
    let RenderPassEncoder _ (WGPURenderPassEncoder e1_ptr) = e1
        RenderPassEncoder _ (WGPURenderPassEncoder e2_ptr) = e2
     in e1_ptr == e2_ptr

instance ToRaw RenderPassEncoder WGPURenderPassEncoder where
  raw = pure . wgpuRenderPassEncoder

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

-- | Operation to perform to the output attachment at the start of a render
-- pass.
data LoadOp a
  = -- | Clear with the specified color value.
    LoadOpClear !a
  | -- | Load from memory.
    LoadOpLoad
  deriving (Eq, Show)

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

-- | Operation to perform to the output attachment at the end of the render
-- pass.
data StoreOp
  = -- | Store the result.
    StoreOpStore
  | -- | Discard the result.
    StoreOpClear
  deriving (Eq, Show)

instance ToRaw StoreOp WGPUStoreOp where
  raw storeOp =
    pure $
      case storeOp of
        StoreOpStore -> WGPUStoreOp.Store
        StoreOpClear -> WGPUStoreOp.Clear

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

data Operations a = Operations
  { load :: !(LoadOp a),
    store :: !StoreOp
  }
  deriving (Eq, Show)

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

-- | Describes a color attachment to a render pass.
data RenderPassColorAttachment = RenderPassColorAttachment
  { -- | The view to use as an attachment.
    colorView :: !TextureView,
    -- | The view that will receive output if multisampling is used.
    resolveTarget :: !(SMaybe TextureView),
    -- | What operations will be performed on this color attachment.
    operations :: !(Operations Color)
  }
  deriving (Eq, Show)

instance ToRaw RenderPassColorAttachment WGPURenderPassColorAttachment where
  raw RenderPassColorAttachment {..} = do
    n_view <- raw colorView
    n_resolveTarget <-
      case resolveTarget of
        SNothing -> pure (WGPUTextureView nullPtr)
        SJust t -> raw t
    n_storeOp <- raw . store $ operations
    (n_loadOp, n_clearColor) <-
      case load operations of
        LoadOpClear color -> do
          n_color <- raw color
          pure (WGPULoadOp.Clear, n_color)
        LoadOpLoad -> do
          n_color <- raw transparentBlack
          pure (WGPULoadOp.Load, n_color)
    pure
      WGPURenderPassColorAttachment.WGPURenderPassColorAttachment
        { view = n_view,
          resolveTarget = n_resolveTarget,
          loadOp = n_loadOp,
          storeOp = n_storeOp,
          clearColor = n_clearColor
        }

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

-- | Describes a depth/stencil attachment to a render pass.
data RenderPassDepthStencilAttachment = RenderPassDepthStencilAttachment
  { -- | The view to use as an attachment.
    depthStencilView :: !TextureView,
    -- | What operations will be performed on the depth part.
    depthOps :: !(SMaybe (Operations Float)),
    -- | What operations will be performed on the stencil part.
    stencilOps :: !(SMaybe (Operations Word32))
  }
  deriving (Eq, Show)

instance
  ToRaw
    RenderPassDepthStencilAttachment
    WGPURenderPassDepthStencilAttachment
  where
  raw RenderPassDepthStencilAttachment {..} = do
    n_view <- raw depthStencilView

    (n_depthLoadOp, n_depthStoreOp, n_clearDepth, n_depthReadOnly) <-
      case depthOps of
        SNothing ->
          pure
            ( WGPULoadOp.Clear,
              WGPUStoreOp.Clear,
              CFloat 0,
              CBool 1
            )
        SJust Operations {..} -> do
          (loadOp, depth) <-
            case load of
              LoadOpClear d -> pure (WGPULoadOp.Clear, CFloat d)
              LoadOpLoad -> pure (WGPULoadOp.Load, CFloat 0)
          storeOp <- raw store
          pure (loadOp, storeOp, depth, CBool 0)

    (n_stencilLoadOp, n_stencilStoreOp, n_clearStencil, n_stencilReadOnly) <-
      case stencilOps of
        SNothing ->
          pure
            ( WGPULoadOp.Clear,
              WGPUStoreOp.Clear,
              0,
              CBool 1
            )
        SJust Operations {..} -> do
          (loadOp, stencil) <-
            case load of
              LoadOpClear s -> pure (WGPULoadOp.Clear, s)
              LoadOpLoad -> pure (WGPULoadOp.Load, 0)
          storeOp <- raw store
          pure (loadOp, storeOp, stencil, CBool 0)

    pure
      WGPURenderPassDepthStencilAttachment.WGPURenderPassDepthStencilAttachment
        { view = n_view,
          depthLoadOp = n_depthLoadOp,
          depthStoreOp = n_depthStoreOp,
          clearDepth = n_clearDepth,
          depthReadOnly = n_depthReadOnly,
          stencilLoadOp = n_stencilLoadOp,
          stencilStoreOp = n_stencilStoreOp,
          clearStencil = n_clearStencil,
          stencilReadOnly = n_stencilReadOnly
        }

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

-- | Describes the attachments of a render pass.
data RenderPassDescriptor = RenderPassDescriptor
  { -- | Debugging label for the render pass.
    renderPassLabel :: !Text,
    -- | Color attachments of the render pass.
    colorAttachments :: !(Vector RenderPassColorAttachment),
    -- | Depth and stencil attachments of the render pass.
    depthStencilAttachment :: !(SMaybe RenderPassDepthStencilAttachment)
  }
  deriving (Eq, Show)

instance ToRaw RenderPassDescriptor WGPURenderPassDescriptor where
  raw RenderPassDescriptor {..} = do
    label_ptr <- rawPtr renderPassLabel
    colorAttachments_ptr <- rawArrayPtr colorAttachments
    depthStencilAttachment_ptr <-
      case depthStencilAttachment of
        SNothing -> pure nullPtr
        SJust x -> rawPtr x
    pure
      WGPURenderPassDescriptor.WGPURenderPassDescriptor
        { nextInChain = nullPtr,
          label = label_ptr,
          colorAttachmentCount = fromIntegral . length $ colorAttachments,
          colorAttachments = colorAttachments_ptr,
          depthStencilAttachment = depthStencilAttachment_ptr,
          occlusionQuerySet = WGPUQuerySet nullPtr
        }

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

-- | Half open range. It includes the 'start' value but not the 'end' value.
data Range a = Range
  { rangeStart :: !a,
    rangeLength :: !a
  }
  deriving (Eq, Show)

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

-- | Begins recording of a render pass.
beginRenderPass ::
  MonadIO m =>
  -- | @CommandEncoder@ to contain the render pass.
  CommandEncoder ->
  -- | Description of the render pass.
  RenderPassDescriptor ->
  -- | IO action which returns the render pass encoder.
  m RenderPassEncoder
beginRenderPass commandEncoder rpd = liftIO . evalContT $ do
  let inst = commandEncoderInst commandEncoder
  renderPassDescriptor_ptr <- rawPtr rpd
  renderPassEncoderRaw <-
    RawFun.wgpuCommandEncoderBeginRenderPass
      (wgpuHsInstance inst)
      (wgpuCommandEncoder commandEncoder)
      renderPassDescriptor_ptr
  pure (RenderPassEncoder inst renderPassEncoderRaw)

-- | Sets the active render pipeline.
--
-- Subsequent draw calls will exhibit the behaviour defined by the pipeline.
renderPassSetPipeline ::
  MonadIO m =>
  -- | Render pass encoder on which to act.
  RenderPassEncoder ->
  -- | Render pipeline to set active.
  RenderPipeline ->
  -- | IO action which sets the active render pipeline.
  m ()
renderPassSetPipeline renderPassEncoder renderPipeline = do
  let inst = renderPassEncoderInst renderPassEncoder
  RawFun.wgpuRenderPassEncoderSetPipeline
    (wgpuHsInstance inst)
    (wgpuRenderPassEncoder renderPassEncoder)
    (wgpuRenderPipeline renderPipeline)

-- | Draws primitives from the active vertex buffers.
renderPassDraw ::
  MonadIO m =>
  -- | Render pass encoder on which to act.
  RenderPassEncoder ->
  -- | Range of vertices to draw.
  Range Word32 ->
  -- | Range of instances to draw.
  Range Word32 ->
  -- | IO action which stores the draw command.
  m ()
renderPassDraw renderPassEncoder vertices instances = do
  let inst = renderPassEncoderInst renderPassEncoder
  RawFun.wgpuRenderPassEncoderDraw
    (wgpuHsInstance inst)
    (wgpuRenderPassEncoder renderPassEncoder)
    (rangeLength (vertices :: Range Word32))
    (rangeLength (instances :: Range Word32))
    (rangeStart vertices)
    (rangeStart instances)

-- | Finish recording of a render pass.
endRenderPass ::
  MonadIO m =>
  -- | Render pass encoder on which to finish recording.
  RenderPassEncoder ->
  -- | IO action that finishes recording.
  m ()
endRenderPass renderPassEncoder = do
  let inst = renderPassEncoderInst renderPassEncoder
  RawFun.wgpuRenderPassEncoderEndPass
    (wgpuHsInstance inst)
    (wgpuRenderPassEncoder renderPassEncoder)