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.Backend.Glslang
import Vulkan.Utils.ShaderQQ.Backend.Internal
import qualified Vulkan.Utils.ShaderQQ.GLSL as GLSL
import qualified Vulkan.Utils.ShaderQQ.HLSL as HLSL
import Vulkan.Utils.ShaderQQ.ShaderType
compileShaderQ
:: Maybe String
-> ShaderType
-> String
-> Maybe String
-> String
-> Q Exp
compileShaderQ :: Maybe String
-> ShaderType -> String -> Maybe String -> String -> Q Exp
compileShaderQ Maybe String
targetEnv ShaderType
shaderType String
stage Maybe String
entryPoint String
code = do
loc <- Q Loc
location
(warnings, result) <- compileShader (Just loc) targetEnv shaderType stage entryPoint code
bs <- messageProcess "glslangValidator" reportWarning fail (warnings, result)
bsToExp bs
compileShader
:: (MonadIO m)
=> Maybe Loc
-> Maybe String
-> ShaderType
-> String
-> Maybe String
-> String
-> m ([GlslangWarning], Either [GlslangError] ByteString)
compileShader :: forall (m :: * -> *).
MonadIO m =>
Maybe Loc
-> Maybe String
-> ShaderType
-> String
-> Maybe String
-> String
-> m ([String], Either [String] ByteString)
compileShader Maybe Loc
loc Maybe String
targetEnv ShaderType
shaderType String
stage Maybe String
entryPoint String
code =
IO ([String], Either [String] ByteString)
-> m ([String], Either [String] ByteString)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ([String], Either [String] ByteString)
-> m ([String], Either [String] ByteString))
-> IO ([String], Either [String] ByteString)
-> m ([String], Either [String] ByteString)
forall a b. (a -> b) -> a -> b
$ String
-> (String -> IO ([String], Either [String] ByteString))
-> IO ([String], Either [String] ByteString)
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> m a) -> m a
withSystemTempDirectory String
"th-shader" ((String -> IO ([String], Either [String] ByteString))
-> IO ([String], Either [String] ByteString))
-> (String -> IO ([String], Either [String] ByteString))
-> IO ([String], Either [String] ByteString)
forall a b. (a -> b) -> a -> b
$ \String
dir -> do
let codeWithLineDirective :: String
codeWithLineDirective =
String -> (Loc -> String) -> Maybe Loc -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
String
code
( case ShaderType
shaderType of
ShaderType
GLSL -> String -> Loc -> String
GLSL.insertLineDirective String
code
ShaderType
HLSL -> String -> Loc -> String
HLSL.insertLineDirective String
code
)
Maybe Loc
loc
let
shader :: String
shader = String
dir String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"/shader." String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
stage
spirv :: String
spirv = String
dir String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"/shader.spv"
String -> String -> IO ()
writeFile String
shader String
codeWithLineDirective
let
targetArgs :: [String]
targetArgs = case Maybe String
targetEnv of
Maybe String
Nothing -> []
Just String
t -> [String
"--target-env", String
t]
shaderTypeArgs :: [String]
shaderTypeArgs = case ShaderType
shaderType of
ShaderType
GLSL -> []
ShaderType
HLSL -> [String
"-D"]
entryPointArgs :: [String]
entryPointArgs = case Maybe String
entryPoint of
Maybe String
Nothing -> []
Just String
name -> case ShaderType
shaderType of
ShaderType
GLSL -> [String
"-e", String
name, String
"--source-entry-point", String
"main"]
ShaderType
HLSL -> [String
"-e", String
name]
args :: [String]
args = [String]
targetArgs [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
shaderTypeArgs [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
entryPointArgs [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
"-S", String
stage, String
"-V", String
shader, String
"-o", String
spirv]
(rc, out, err) <- ProcessConfig () () () -> IO (ExitCode, ByteString, ByteString)
forall (m :: * -> *) stdin stdoutIgnored stderrIgnored.
MonadIO m =>
ProcessConfig stdin stdoutIgnored stderrIgnored
-> m (ExitCode, ByteString, ByteString)
readProcess (ProcessConfig () () () -> IO (ExitCode, ByteString, ByteString))
-> ProcessConfig () () () -> IO (ExitCode, ByteString, ByteString)
forall a b. (a -> b) -> a -> b
$ String -> [String] -> ProcessConfig () () ()
proc String
"glslangValidator" [String]
args
let (warnings, errors) = processGlslangMessages (out <> err)
case rc of
ExitCode
ExitSuccess -> do
bs <- String -> IO ByteString
BS.readFile String
spirv
pure (warnings, Right bs)
ExitFailure Int
_rc -> ([String], Either [String] ByteString)
-> IO ([String], Either [String] ByteString)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([String]
warnings, [String] -> Either [String] ByteString
forall a b. a -> Either a b
Left [String]
errors)