vulkan-utils-0.5.0: src/Vulkan/Utils/ShaderQQ/Backend/Glslang/Internal.hs
module Vulkan.Utils.ShaderQQ.Backend.Glslang.Internal
( compileShaderQ
, compileShader
) where
import Control.Monad.IO.Class
import Data.ByteString ( ByteString )
import qualified Data.ByteString as BS
import Data.FileEmbed
import Language.Haskell.TH
import System.Exit
import System.IO.Temp
import System.Process.Typed
import Vulkan.Utils.ShaderQQ.ShaderType
import qualified Vulkan.Utils.ShaderQQ.GLSL as GLSL
import qualified Vulkan.Utils.ShaderQQ.HLSL as HLSL
import Vulkan.Utils.ShaderQQ.Backend.Glslang
import Vulkan.Utils.ShaderQQ.Backend.Internal
-- * Utilities
-- | Compile a GLSL/HLSL shader to spir-v using glslangValidator.
--
-- Messages are converted to GHC warnings or errors depending on compilation success.
compileShaderQ
:: Maybe String
-- ^ Argument to pass to `--target-env`
-> ShaderType
-- ^ Argument to specify between glsl/hlsl shader
-> String
-- ^ stage
-> Maybe String
-- ^ Argument to specify entry-point function name
-> String
-- ^ glsl or hlsl shader code
-> Q Exp
-- ^ Spir-V bytecode
compileShaderQ targetEnv shaderType stage entryPoint code = do
loc <- location
(warnings, result) <- compileShader (Just loc) targetEnv shaderType stage entryPoint code
bs <- messageProcess "glslangValidator" reportWarning fail (warnings, result)
bsToExp bs
-- | Compile a GLSL/HLSL shader to spir-v using glslangValidator
compileShader
:: MonadIO m
=> Maybe Loc
-- ^ Source location
-> Maybe String
-- ^ Argument to pass to `--target-env`
-> ShaderType
-- ^ Argument to specify between glsl/hlsl shader
-> String
-- ^ stage
-> Maybe String
-- ^ Argument to specify entry-point function name
-> String
-- ^ glsl or hlsl shader code
-> m ([GlslangWarning], Either [GlslangError] ByteString)
-- ^ Spir-V bytecode with warnings or errors
compileShader loc targetEnv shaderType stage entryPoint code =
liftIO $ withSystemTempDirectory "th-shader" $ \dir -> do
let codeWithLineDirective = maybe code (case shaderType of
GLSL -> GLSL.insertLineDirective code
HLSL -> HLSL.insertLineDirective code
) loc
let shader = dir <> "/shader." <> stage
spirv = dir <> "/shader.spv"
writeFile shader codeWithLineDirective
let targetArgs = case targetEnv of
Nothing -> []
Just t -> ["--target-env", t]
shaderTypeArgs = case shaderType of
GLSL -> []
HLSL -> ["-D"]
-- https://github.com/KhronosGroup/glslang/issues/1045#issuecomment-328707953
entryPointArgs = case entryPoint of
Nothing -> []
Just name -> case shaderType of
GLSL -> ["-e", name, "--source-entry-point", "main"]
HLSL -> ["-e", name]
args = targetArgs ++ shaderTypeArgs ++ entryPointArgs ++ ["-S", stage, "-V", shader, "-o", spirv]
(rc, out, err) <- readProcess $ proc "glslangValidator" args
let (warnings, errors) = processGlslangMessages (out <> err)
case rc of
ExitSuccess -> do
bs <- BS.readFile spirv
pure (warnings, Right bs)
ExitFailure _rc -> pure (warnings, Left errors)