packages feed

fir-0.1.0.0: src/SPIRV/Requirements.hs

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE ViewPatterns    #-}

{-|
Module: SPIRV.Requrements

Functions computing capability/extension requirements
for various circumstances.

-}

module SPIRV.Requirements where

-- base
import Data.Bits
  ( Bits(testBit) )
import Data.Word
  ( Word32 )

-- containers
import Data.Set
  ( Set )
import qualified Data.Set as Set
  ( insert, unions )

-- fir
import SPIRV.Capability
import SPIRV.Extension
import qualified SPIRV.Image    as Image
import           SPIRV.Operation
  hiding ( Capability )
import           SPIRV.PrimOp
  ( PrimOp(..), VecPrimOp(..), GroupPrimOp(..), BitPrimOp(ReverseBits)
  , op
  )
import           SPIRV.PrimTy
  ( PrimTy, scalars )
import qualified SPIRV.PrimTy   as PrimTy
import           SPIRV.ScalarTy
  ( ScalarTy, Width(..) )
import qualified SPIRV.ScalarTy as ScalarTy
import           SPIRV.Stage
  ( ExecutionModel(Stage), Stage(..), Shader, Backend(..) )
import qualified SPIRV.Stage    as Stage
  ( ExecutionModel(..), Shader(..) )
import           SPIRV.Version
  ( Version(..) )

--------------------------------------------------------------------------
-- primitive operations

primOpCapabilities :: Backend -> PrimOp -> Set Capability
primOpCapabilities bk ( op bk -> SatConvertSToU ) = [ ComputeKernel ]
primOpCapabilities bk ( op bk -> SatConvertUToS ) = [ ComputeKernel ]
primOpCapabilities _  ( MatOp {}                ) = [ Matrix ]
primOpCapabilities _  ( RayOp {}                ) = [ RayTracingKHR ]
primOpCapabilities bk ( BitOp ReverseBits _     ) =
  case bk of
    SPIRV.Stage.Vulkan -> [ ] -- (requires the Shader capability)
    SPIRV.Stage.OpenCL -> [ BitInstructions ]
primOpCapabilities bk ( GroupOp gpOp            ) = groupOpCapabilities bk gpOp
primOpCapabilities bk ( VecOp ( Vectorise primop ) _ _ ) = primOpCapabilities bk primop
primOpCapabilities _  _                           = [ ]

groupOpCapabilities :: Backend -> GroupPrimOp -> Set Capability
groupOpCapabilities bk _ =
  case bk of
    SPIRV.Stage.OpenCL -> [ Groups ]
    SPIRV.Stage.Vulkan -> [ GroupNonUniformArithmetic ]

primOpExtensions :: Backend -> PrimOp -> Set Extension
primOpExtensions SPIRV.Stage.OpenCL ( BitOp ReverseBits _ ) = [ SPV_KHR_bit_instructions ]
primOpExtensions bk ( VecOp ( Vectorise primop ) _ _ ) = primOpExtensions bk primop
primOpExtensions _  _                                  = [ ]

-- | Why the backend doesn't support a primitive operation (regardless of capabilities,
-- extensions and SPIR-V version), if it doesn't.
primOpUnsupported :: Backend -> PrimOp -> Maybe String
primOpUnsupported SPIRV.Stage.Vulkan ( BitOp ReverseBits s )
  | ScalarTy.scalarWidth s /= W32
  = Just "Vulkan only supports OpBitReverse on 32-bit integers"
primOpUnsupported bk ( VecOp ( Vectorise primop ) _ _ ) = primOpUnsupported bk primop
primOpUnsupported _  _                                  = Nothing

primOpMinVersion :: Backend -> PrimOp -> Version
primOpMinVersion SPIRV.Stage.Vulkan ( GroupOp {} ) = Version 1 3
primOpMinVersion _                  _              = Version 1 0

--------------------------------------------------------------------------
-- types

scalarCapabilities :: ScalarTy -> Set Capability
scalarCapabilities (ScalarTy.Integer _ W8 ) = [ Int8  ]
scalarCapabilities (ScalarTy.Integer _ W16) = [ Int16 ]
scalarCapabilities (ScalarTy.Integer _ W64) = [ Int64 ]
scalarCapabilities (ScalarTy.Integer _ _  ) = [ ]
scalarCapabilities (ScalarTy.Floating  W16) = [ Float16 ]
scalarCapabilities (ScalarTy.Floating  W64) = [ Float64 ]
scalarCapabilities (ScalarTy.Floating  _  ) = [ ]

primTyCapabilities :: PrimTy -> Set Capability
primTyCapabilities ( PrimTy.Matrix _ _ ty )
  = Set.insert Matrix    $ scalarCapabilities ty
primTyCapabilities ( PrimTy.Vector n   ty )
  | n > 4     = Set.insert Vector16 $ primTyCapabilities ty
  | otherwise =                       primTyCapabilities ty
primTyCapabilities ( PrimTy.Image img )
  = imageCapabilities img <> scalarCapabilities ( Image.texelComponent img )
primTyCapabilities ( PrimTy.SampledImage img )
  = imageCapabilities img <> scalarCapabilities ( Image.texelComponent img )
primTyCapabilities ty
  = Set.unions ( map scalarCapabilities ( PrimTy.scalars ty) )

-- types when present in an interface
globalCapabilities :: PrimTy -> Set Capability
globalCapabilities = primTyCapabilities

globalScalarExtensions :: ScalarTy -> Set Extension
globalScalarExtensions (ScalarTy.Integer _ W8 ) = [ SPV_KHR_8bit_storage  ]
globalScalarExtensions (ScalarTy.Integer _ W16) = [ SPV_KHR_16bit_storage ]
globalScalarExtensions (ScalarTy.Integer _ _  ) = [ ]
globalScalarExtensions (ScalarTy.Floating  W16) = [ SPV_KHR_16bit_storage ]
globalScalarExtensions (ScalarTy.Floating  _  ) = [ ]

globalExtensions :: PrimTy -> Set Extension
globalExtensions ty = Set.unions ( map globalScalarExtensions ( scalars ty ) )

--------------------------------------------------------------------------
-- stages / execution models

shaderCapabilities :: Shader -> Set Capability
shaderCapabilities Stage.VertexShader                 = [ Shader       ]
shaderCapabilities Stage.TessellationControlShader    = [ Tessellation ] --- |
shaderCapabilities Stage.TessellationEvaluationShader = [ Tessellation ] --- |-- shader capability implied
shaderCapabilities Stage.GeometryShader               = [ Geometry     ] --- |
shaderCapabilities Stage.FragmentShader               = [ Shader       ]
shaderCapabilities Stage.ComputeShader                = [ Shader       ]

stageCapabilities :: Stage -> Set Capability
stageCapabilities (ShaderStage s) = shaderCapabilities s
stageCapabilities (MeshStage   _) = [ MeshShadingNV ]
stageCapabilities (RayStage    _) = [ RayTracingKHR ]

executionModelCapabilities :: ExecutionModel -> Set Capability
executionModelCapabilities (Stage s)    = stageCapabilities s
executionModelCapabilities Stage.Kernel = [ ComputeKernel, Addresses ]

stageExtensions :: Stage -> Set Extension
stageExtensions (ShaderStage _) = [ ]
stageExtensions (MeshStage   _) = [ SPV_NV_mesh_shader ]
stageExtensions (RayStage    _) = [ SPV_KHR_ray_tracing ]

executionModelExtensions :: ExecutionModel -> Set Extension
executionModelExtensions (Stage s)    = stageExtensions s
executionModelExtensions Stage.Kernel = [ ]

--------------------------------------------------------------------------
-- images, image formats, image properties

formatCapabilities :: Image.ImageFormat Word32 -> Set Capability
formatCapabilities format
  = case Image.requiredFormatUsage format of
      Just Image.Storage   -> [ Shader, StorageImageExtendedFormats ]
      _                    -> [ Shader ]

-- | Capabilities required to read from an image of the given dimensionality
-- and format using @OpImageRead@.
unknownFormatReadCapabilities :: Image.Dimensionality -> Maybe (Image.ImageFormat Word32) -> Set Capability
unknownFormatReadCapabilities Image.SubpassData _       = [ ]
unknownFormatReadCapabilities _                 Nothing = [ StorageImageReadWithoutFormat ]
unknownFormatReadCapabilities _                 _       = [ ]

-- | Capabilities required to write to an image of the given format
-- using @OpImageWrite@.
unknownFormatWriteCapabilities :: Maybe (Image.ImageFormat Word32) -> Set Capability
unknownFormatWriteCapabilities Nothing = [ StorageImageWriteWithoutFormat ]
unknownFormatWriteCapabilities _       = [ ]

-- | Capabilities required to declare an image type.
imageCapabilities :: Image.Image -> Set Capability
imageCapabilities img
  =  foldMap formatCapabilities ( Image.imageFormat img )
  <> dimCapabilities ( Image.imageUsage img ) ( Image.dimensionality img ) ( Image.arrayness img )
  <> msCapabilities  ( Image.imageUsage img ) ( Image.arrayness img ) ( Image.multiSampling img )

dimCapabilities :: Maybe Image.ImageUsage -> Image.Dimensionality -> Image.Arrayness -> Set Capability
dimCapabilities _                    Image.SubpassData _             = [ Shader, InputAttachment ]
dimCapabilities (Just Image.Storage) Image.Rect        _             = [ Shader, SampledRect, ImageRect ]
dimCapabilities _                    Image.Rect        _             = [ Shader, SampledRect ]
dimCapabilities (Just Image.Storage) Image.OneD        _             = [ Sampled1D, Image1D ]
dimCapabilities _                    Image.OneD        _             = [ Sampled1D ]
dimCapabilities (Just Image.Storage) Image.Cube        Image.Arrayed = [ SampledCubeArray, ImageCubeArray ]
dimCapabilities _                    Image.Cube        Image.Arrayed = [ SampledCubeArray ]
dimCapabilities (Just Image.Storage) Image.Buffer      _             = [ SampledBuffer, ImageBuffer ]
dimCapabilities _                    Image.Buffer      _             = [ SampledBuffer ]
dimCapabilities _                    _                 _             = [ ]

msCapabilities :: Maybe Image.ImageUsage -> Image.Arrayness -> Image.MultiSampling -> Set Capability
msCapabilities (Just Image.Storage) Image.Arrayed    Image.MultiSampled = [ StorageImageMultisample, ImageMSArray ]
msCapabilities (Just Image.Storage) Image.NonArrayed Image.MultiSampled = [ StorageImageMultisample ]
msCapabilities _                    _                _                  = [ ]

-- lod capability from image operand bitmask
-- (note that the bitmask is shifted up one byte from what SPIR-V uses)
lodCapabilities :: Backend -> Word32 -> Set Capability
lodCapabilities bk bm
  -- uses the LOD operand
  | bm `testBit` 9
  && bk == SPIRV.Stage.OpenCL
  = [ ImageBasic, ImageMipmap ]
  -- uses the MinLOD opeand
  | bm `testBit` 15
  = [ MinLod ]
  | otherwise
  = [ ]

-- whether the ImageGatherExtended capability is required
gatherCapabilities :: Word32 -> Set Capability
gatherCapabilities bm
  |  bm `testBit` 12 -- Offset operand
  || bm `testBit` 13 -- ConstOffsets operand
  = [ ImageGatherExtended ]
  | otherwise
  = [ ]