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 ) )