packages feed

fir-0.1.0.0: src/FIR/Syntax/Images.hs

{-# LANGUAGE AllowAmbiguousTypes    #-}
{-# LANGUAGE ConstraintKinds        #-}
{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE GADTs                  #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE PatternSynonyms        #-}
{-# LANGUAGE PolyKinds              #-}
{-# LANGUAGE ScopedTypeVariables    #-}
{-# LANGUAGE TypeApplications       #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE UndecidableInstances   #-}

{-|
Module: FIR.Syntax.Images 

This module allows for the operations 'FIR.Syntax.Program.use'
and 'FIR.Syntax.Program.assign' to work with images, by providing the 'ImageTexel' lens.

This allows accessing image data in a shader like so:

@ use \@(ImageTexel "imageName") imageOperands coords @

provided that @"imageName"@ binds an image in the monadic context.

@coords@ are the coordinates at which to access the image.
The specific type of these coordinates depends on:

  * whether we are sampling or reading directly,
  * the image dimensionality,
  * whether the image is arrayed,
  * the type of coordinates being used (affine or projective).

The return type of this operation depends on:

  * the image format (for instance a normalised integer image format
    will return floating-point types),
  * whether a depth-comparison is being performed: the return type
    will be a scalar in that case, and otherwise a vector with 4 components.


These properties are specified either at the type level,
with a top-level type annotation declaring the image properties,
or by the 'FIR.Prim.Image.ImageOperands' @imageOperands@ (optional
unless a depth-comparison value is required, but can be used
e.g. to set an explicit level of detail).



As images in @SPIR-V@ form an opaque type, the corresponding
'FIR.Prim.Image.Image' data type used by this library is
uninhabited, meaning it is impossible to construct or obtain
a value of this type. Instead, 'FIR.Prim.Image.Image'
is used only indirectly.
Attempting to directly obtain an 'FIR.Prim.Image.Image'
results in an error:

> img <- use @(Name "imgName")
>
>   • Variable named "imgName" refers to an image.
>     To access image data, use the 'ImageTexel' optic or the 'imageRead' function.


-}

module FIR.Syntax.Images
  ( -- * Lens focusing on an image texel
    ImageTexel

  -- * Image query operations
  , imageSizeLOD, imageSize, imageLOD, imageLevels, imageSamples

  )
where

-- base
import Prelude
  hiding ( Floating, Integral )
import Data.Kind
  ( Type )
import Data.Proxy
  ( Proxy(..) )
import Data.Word
  ( Word32 )
import GHC.TypeLits
  ( Symbol, KnownSymbol )

-- fir
import Control.Type.Optic
  ( Optic(Field_, RTOptic_, ComposeO)
  , Gettable, Settable
  )
import Data.Type.Known
  ( Known )
import FIR.AST
  ( Code, Syntactic(fromAST)
  , pattern QuerySize, pattern QuerySizeLOD
  , pattern QueryLOD, pattern QueryLevels, pattern QuerySamples
  )
import FIR.Module
  ( Program )
import FIR.Prim.Image
  ( ImageProperties
  , Image
  , OperandName, ImageOperands
  )
import FIR.Prim.Types
  ( PrimTy )
import FIR.ProgramState
  ( ProgramState )
import FIR.Validation.Images
  ( LookupImageProperties
  , ValidImageRead, ValidImageWrite
  , ImageTexelType
  , ValidQueryImageSize, ValidQueryImageSizeLOD
  , ValidQueryImageLOD, ValidQueryImageLevels, ValidQueryImageSamples
  )

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

-- | Lens for focusing on texels of an image.
--
-- This is a /phantom/ composite @ Name k :.: Texel @,
-- where @Texel@ denotes a (hypothetical) run-time optic
-- which focuses on the texel at a given coordinate (with specified image operands).
--
-- As it is impossible to obtain actual values of type 'FIR.Prim.Image.Image',
-- it is only the composite optic @ImageTexel k@, understood atomically,
-- which allows focusing on image texels.
type family ImageTexel
              ( k :: Symbol )
            = ( optic
                :: Optic
                    '[ ImageOperands props ops, imgCds ]
                    ( i :: ProgramState )
                    ( ImageTexelType props ops )
              )
            | optic -> k
            where
  forall i props ops imgCds k.
    ImageTexel k
      = ( ( ( Field_ (k :: Symbol) :: Optic '[] i (Image props) )
            `ComposeO`
            ( RTOptic_
                :: Optic
                    '[ ImageOperands props ops, imgCds ]
                    ( Image props )
                    ( ImageTexelType props ops )
            )
          ) :: Optic
                '[ ImageOperands props ops, imgCds ]
                i
                (ImageTexelType props ops)
        )

----------------------------------------------------
-- instances for this particular 'ghost' composite
-- (leverages that normal runtime optics only ever have 1 run-time index, this has 2)

instance {-# OVERLAPPING #-} 
         forall 
           ( k        :: Symbol          )
           ( i        :: ProgramState    )
           ( props    :: ImageProperties )
           ( ops      :: [OperandName]   )
           ( empty    :: [Type]          )
           ( imgOps   :: Type            )
           ( imgCds   :: Type            )
           ( imgTexel :: Type            )
         .
         ( KnownSymbol k
         , LookupImageProperties k i ~ props
         , Known ImageProperties props
         , ValidImageRead props ops imgCds
         , empty ~ '[]
         , imgOps ~ ImageOperands props ops
         , imgTexel ~ ImageTexelType props ops
         )
      => Gettable ( ( ( Field_ k :: Optic empty i (Image props))
                    `ComposeO`
                      ( RTOptic_ :: Optic '[imgOps, imgCds] (Image props) imgTexel )
                    ) :: Optic '[imgOps, imgCds] i imgTexel
                  )
      where

instance {-# OVERLAPPING #-} 
         forall 
           ( k        :: Symbol          )
           ( i        :: ProgramState    )
           ( props    :: ImageProperties )
           ( ops      :: [OperandName]   )
           ( empty    :: [Type]          )
           ( imgOps   :: Type            )
           ( imgCds   :: Type            )
           ( imgTexel :: Type            )
         .
         ( KnownSymbol k
         , LookupImageProperties k i ~ props
         , Known ImageProperties props
         , ValidImageWrite props ops imgCds
         , empty ~ '[]
         , imgOps ~ ImageOperands props ops
         , imgTexel ~ ImageTexelType props ops
         )
      => Settable ( ( ( Field_ k :: Optic empty i (Image props))
                    `ComposeO`
                      ( RTOptic_ :: Optic '[imgOps, imgCds] (Image props) imgTexel )
                    ) :: Optic '[imgOps, imgCds] i imgTexel
                  )
      where

-----------------------------------------------------------------------
-- Image query operations.

-- | Query the size of the named image, at a given level-of-detail.
imageSizeLOD
  :: forall
      ( imgName :: Symbol )
      ( res     :: Type   )
      ( i       :: ProgramState )
  . ( KnownSymbol imgName
    , PrimTy res
    , ValidQueryImageSizeLOD imgName i res
    )
  => Code Word32 -- ^ Image level-of-detail to use for the query.
  -> Program i i ( Code res )
imageSizeLOD = fromAST ( QuerySizeLOD ( Proxy @imgName ) ( Proxy @res ) )

-- | Query the size of the named image.
imageSize
  :: forall
      ( imgName :: Symbol )
      ( res     :: Type   )
      ( i       :: ProgramState )
  . ( KnownSymbol imgName
    , PrimTy res
    , ValidQueryImageSize imgName i res
    )
  => Program i i ( Code res )
imageSize = fromAST ( QuerySize ( Proxy @imgName ) ( Proxy @res ) )

-- | Query the level of detail of the named image at the given coordinates.
imageLOD
  :: forall
      ( imgName :: Symbol )
      ( coords  :: Type   )
      ( res     :: Type   )
      ( i       :: ProgramState )
  . ( KnownSymbol imgName
    , PrimTy res
    , ValidQueryImageLOD imgName i coords res
    )
  => Code coords
  -> Program i i ( Code res )
imageLOD = fromAST ( QueryLOD ( Proxy @imgName ) ( Proxy @res ) )

-- | Query how many levels of detail the named image has.
imageLevels
  :: forall
      ( imgName :: Symbol )
      ( res     :: Type   )
      ( i       :: ProgramState )
  . ( KnownSymbol imgName
    , PrimTy res
    , ValidQueryImageLevels imgName i res
    )
  => Program i i ( Code res )
imageLevels = fromAST ( QueryLevels ( Proxy @imgName ) ( Proxy @res ) )

-- | Query how many samples the named multi-sampling image has.
imageSamples
  :: forall
      ( imgName :: Symbol )
      ( res     :: Type   )
      ( i       :: ProgramState )
  . ( KnownSymbol imgName
    , PrimTy res
    , ValidQueryImageSamples imgName i res
    )
  => Program i i ( Code res )
imageSamples = fromAST ( QuerySamples ( Proxy @imgName ) ( Proxy @res ) )