vulkan-utils 0.4.2 → 0.5.0
raw patch · 17 files changed
+1279/−505 lines, 17 files
Files
- changelog.md +16/−0
- package.yaml +1/−1
- src/Vulkan/Utils/ShaderQQ.hs +0/−216
- src/Vulkan/Utils/ShaderQQ/Backend/Glslang.hs +23/−0
- src/Vulkan/Utils/ShaderQQ/Backend/Glslang/Internal.hs +90/−0
- src/Vulkan/Utils/ShaderQQ/Backend/Internal.hs +33/−0
- src/Vulkan/Utils/ShaderQQ/Backend/Shaderc.hs +97/−0
- src/Vulkan/Utils/ShaderQQ/Backend/Shaderc/Internal.hs +87/−0
- src/Vulkan/Utils/ShaderQQ/GLSL.hs +59/−0
- src/Vulkan/Utils/ShaderQQ/GLSL/Glslang.hs +202/−0
- src/Vulkan/Utils/ShaderQQ/GLSL/Shaderc.hs +198/−0
- src/Vulkan/Utils/ShaderQQ/HLSL.hs +42/−0
- src/Vulkan/Utils/ShaderQQ/HLSL/Glslang.hs +199/−0
- src/Vulkan/Utils/ShaderQQ/HLSL/Shaderc.hs +199/−0
- src/Vulkan/Utils/ShaderQQ/ShaderType.hs +20/−0
- src/Vulkan/Utils/ShaderQQ/Shaderc.hs +0/−285
- vulkan-utils.cabal +13/−3
changelog.md view
@@ -2,6 +2,22 @@ ## WIP +## [0.5.0] - 2021-02-24+- Refactor module `Vulkan.Utils.ShaderQQ`+ - Remove `Vulkan.Utils.ShaderQQ`+ - Remove `Vulkan.Utils.ShaderQQ.Shaderc`+ - Provide `glsl`/`hlsl`, `vert` .. `comp`, `rgen` .. `rcall`, `mesh`, `task`, `compileShaderQ`, `compileShader` in each ShaderQQ provider module under `Vulkan.Utils.ShaderQQ` for help compile shaders+ - Add `Vulkan.Utils.ShaderQQ.Backend.Glslang`to help process warning & error messages for glslangValidator+ - Add `Vulkan.Utils.ShaderQQ.Backend.Shaderc`to help process warning & error messages for glslc+ - Add `Vulkan.Utils.ShaderQQ.GLSL.Glslang` to compile glsl shaders for glslangValidator+ - Add `Vulkan.Utils.ShaderQQ.GLSL.Shaderc` to compile glsl shaders for glslc+ - Add `Vulkan.Utils.ShaderQQ.HLSL.Glslang` to compile hlsl shaders for glslangValidator+ - Add `Vulkan.Utils.ShaderQQ.HLSL.Shaderc` to compile hlsl shaders for glslc+- specify `--target-spv` for shaderc ray tracing shaders+- specify `--target-env` for glslang ray tracing shaders+- support pass hlsl entry point to glslangValidator and shaderc+- support pass glsl entry point to glslangValidator+ ## [0.4.2] - 2021-02-18 - Relax bounds on `vulkan`
package.yaml view
@@ -1,5 +1,5 @@ name: vulkan-utils-version: "0.4.2"+version: "0.5.0" synopsis: Utils for the vulkan package category: Graphics maintainer: Joe Hermaszewski <live.long.and.prosper@monoid.al>
− src/Vulkan/Utils/ShaderQQ.hs
@@ -1,216 +0,0 @@-module Vulkan.Utils.ShaderQQ- ( glsl- , comp- , frag- , geom- , tesc- , tese- , vert- , GLSLError- , GLSLWarning- , compileShaderQ- , compileShader- , processValidatorMessages- ) where--import Control.Monad.IO.Class-import Data.ByteString ( ByteString )-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy.Char8 as BSL-import Data.Char-import Data.FileEmbed-import Data.List.Extra-import Language.Haskell.TH-import Language.Haskell.TH.Quote-import System.Exit-import System.FilePath-import System.IO.Temp-import System.Process.Typed-import Vulkan.Utils.Internal ( badQQ )-import Vulkan.Utils.ShaderQQ.Interpolate---- $setup--- >>> :set -XQuasiQuotes---- | 'glsl' is a QuasiQuoter which produces GLSL source code with @#line@--- directives inserted so that error locations point to the correct location in--- the Haskell source file. It also permits basic string interpolation.------ - Interpolated variables are prefixed with @$@--- - They can optionally be surrounded with braces like @${foo}@--- - Interpolated variables are converted to strings with 'show'--- - To escape a @$@ use @\\$@------ It is intended to be used in concert with 'compileShaderQ' like so------ @--- myConstant = 3.141 -- Note that this will have to be in a different module--- myFragmentShader = $(compileShaderQ "frag" [glsl|--- #version 450--- const float myConstant = ${myConstant};--- main (){--- }--- |])--- @------ An explicit example (@<interactive>@ is from doctest):------ >>> let version = 450 :: Int in [glsl|#version $version|]--- "#version 450\n#extension GL_GOOGLE_cpp_style_line_directive : enable\n#line 46 \"<interactive>\"\n"------ Note that line number will be thrown off if any of the interpolated--- variables contain newlines.-glsl :: QuasiQuoter-glsl = (badQQ "glsl")- { quoteExp = \s -> do- loc <- location- -- Insert the directive here, `compileShaderQ` will insert- -- another one, but it's before this one, so who cares.- let codeWithLineDirective = insertLineDirective s loc- interpExp codeWithLineDirective- }---- | QuasiQuoter for creating a compute shader.------ Equivalent to calling @$(compileShaderQ "comp" [glsl|...|])@ without--- interpolation support.-comp :: QuasiQuoter-comp = shaderQQ "comp"---- | QuasiQuoter for creating a fragment shader.------ Equivalent to calling @$(compileShaderQ "frag" [glsl|...|])@ without--- interpolation support.-frag :: QuasiQuoter-frag = shaderQQ "frag"---- | QuasiQuoter for creating a geometry shader.------ Equivalent to calling @$(compileShaderQ "geom" [glsl|...|])@ without--- interpolation support.-geom :: QuasiQuoter-geom = shaderQQ "geom"---- | QuasiQuoter for creating a tessellation control shader.------ Equivalent to calling @$(compileShaderQ "tesc" [glsl|...|])@ without--- interpolation support.-tesc :: QuasiQuoter-tesc = shaderQQ "tesc"---- | QuasiQuoter for creating a tessellation evaluation shader.------ Equivalent to calling @$(compileShaderQ "tese" [glsl|...|])@ without--- interpolation support.-tese :: QuasiQuoter-tese = shaderQQ "tese"---- | QuasiQuoter for creating a vertex shader.------ Equivalent to calling @$(compileShaderQ "vert" [glsl|...|])@ without--- interpolation support.-vert :: QuasiQuoter-vert = shaderQQ "vert"--shaderQQ :: String -> QuasiQuoter-shaderQQ stage = (badQQ stage) { quoteExp = compileShaderQ Nothing stage }---- * Utilities---- | Compile a glsl 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`- -> String- -- ^ stage- -> String- -- ^ glsl code- -> Q Exp- -- ^ Spir-V bytecode-compileShaderQ targetEnv stage code = do- loc <- location- (warnings, result) <- compileShader (Just loc) targetEnv stage code- case warnings of- [] -> pure ()- _some -> reportWarning $ prepare warnings-- bs <- case result of- Left [] -> fail "glslangValidator failed with no errors"- Left errors -> do- reportError $ prepare errors- pure mempty- Right bs -> pure bs-- bsToExp bs-- where- prepare [singleLine] = singleLine- prepare multiline =- intercalate "\n" $ "glslangValidator:" : map (mappend " ") multiline--type GLSLError = String-type GLSLWarning = String---- | Compile a glsl shader to spir-v using glslangValidator-compileShader- :: MonadIO m- => Maybe Loc- -- ^ Source location- -> Maybe String- -- ^ Argument to pass to `--target-env`- -> String- -- ^ stage- -> String- -- ^ glsl code- -> m ([GLSLWarning], Either [GLSLError] ByteString)- -- ^ Spir-V bytecode with warnings or errors-compileShader loc targetEnv stage code =- liftIO $ withSystemTempDirectory "th-shader" $ \dir -> do- let codeWithLineDirective = maybe code (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]- args = targetArgs ++ ["-S", stage, "-V", shader, "-o", spirv]- (rc, out, err) <- readProcess $ proc "glslangValidator" args- let (warnings, errors) = processValidatorMessages (out <> err)- case rc of- ExitSuccess -> do- bs <- BS.readFile spirv- pure (warnings, Right bs)- ExitFailure _rc -> pure (warnings, Left errors)--processValidatorMessages :: BSL.ByteString -> ([GLSLWarning], [GLSLError])-processValidatorMessages =- foldr grep ([], []) . filter (not . null) . lines . BSL.unpack- where- grep line (ws, es) | "WARNING: " `isPrefixOf` line = (cut line : ws, es)- | "ERROR: " `isPrefixOf` line = (ws, cut line : es)- | otherwise = (ws, es)-- cut line = takeFileName path <> msg- where (path, msg) = break (== ':') . drop 1 $ dropWhile (/= ' ') line---- If possible, insert a #line directive after the #version directive (as well--- as the extension which allows filenames in line directives.-insertLineDirective :: String -> Loc -> String-insertLineDirective code Loc {..} =- let isVersionDirective = ("#version" `isPrefixOf`) . dropWhile isSpace- codeLines = lines code- (beforeVersion, afterVersion) = break isVersionDirective codeLines- lineDirective =- [ "#extension GL_GOOGLE_cpp_style_line_directive : enable"- , "#line "- <> show (fst loc_start + length beforeVersion + 1)- <> " \""- <> loc_filename- <> "\""- ]- in case afterVersion of- [] -> code- v : xs -> unlines $ beforeVersion <> [v] <> lineDirective <> xs
+ src/Vulkan/Utils/ShaderQQ/Backend/Glslang.hs view
@@ -0,0 +1,23 @@+module Vulkan.Utils.ShaderQQ.Backend.Glslang+ ( GlslangError+ , GlslangWarning+ , processGlslangMessages+ ) where++import qualified Data.ByteString.Lazy.Char8 as BSL+import Data.List.Extra+import System.FilePath++type GlslangError = String+type GlslangWarning = String++processGlslangMessages :: BSL.ByteString -> ([GlslangWarning], [GlslangError])+processGlslangMessages =+ foldr grep ([], []) . filter (not . null) . lines . BSL.unpack+ where+ grep line (ws, es) | "WARNING: " `isPrefixOf` line = (cut line : ws, es)+ | "ERROR: " `isPrefixOf` line = (ws, cut line : es)+ | otherwise = (ws, es)++ cut line = takeFileName path <> msg+ where (path, msg) = break (== ':') . drop 1 $ dropWhile (/= ' ') line
+ src/Vulkan/Utils/ShaderQQ/Backend/Glslang/Internal.hs view
@@ -0,0 +1,90 @@+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)
+ src/Vulkan/Utils/ShaderQQ/Backend/Internal.hs view
@@ -0,0 +1,33 @@+module Vulkan.Utils.ShaderQQ.Backend.Internal+ ( messageProcess+ ) where++import Data.ByteString ( ByteString )+import Data.List.Extra++messageProcess+ :: (Applicative m, Monad m)+ => String+ -- ^ tool name+ -> (String -> m ())+ -- ^ warning+ -> (String -> m ByteString)+ -- ^ error+ -> ([String], Either [String] ByteString)+ -- ^ Spir-V bytecode with warnings or errors+ -> m ByteString+ -- ^ Spir-V bytecode+messageProcess tool warn err (warnings, result) = do+ case warnings of+ [] -> pure ()+ _some -> warn $ prepare warnings+ case result of+ Left [] -> err $ tool ++ " failed with no errors"+ Left errors -> do+ _ <- err $ prepare errors+ pure mempty+ Right bs -> pure bs+ where+ prepare [singleLine] = singleLine+ prepare multiline =+ intercalate "\n" $ (tool ++ ":") : map (mappend " ") multiline
+ src/Vulkan/Utils/ShaderQQ/Backend/Shaderc.hs view
@@ -0,0 +1,97 @@+module Vulkan.Utils.ShaderQQ.Backend.Shaderc+ ( ShadercError+ , ShadercWarning+ , processShadercMessages+ ) where++import Control.Monad ( void )+import qualified Data.ByteString.Lazy.Char8 as BSL+import Data.Foldable ( asum )+import Text.ParserCombinators.ReadP++type ShadercError = String+type ShadercWarning = String++processShadercMessages :: BSL.ByteString -> ([ShadercWarning], [ShadercError])+processShadercMessages = foldMap parseMsg . lines . BSL.unpack++-- >>> parseMsg "blah"+-- ([],[])+--+-- >>> parseMsg "blah"+-- ([],["blah"])+--+-- >>> parseMsg "foo:2: error: unknown var"+-- ([],["foo:2: unknown var"])+--+-- >>> parseMsg "foo:2: warning: unknown var"+-- (["foo:2: unknown var"],[])+--+-- >>> parseMsg "bar:2: error: 'a' : unknown variable"+-- ([],["bar:2: 'a' : unknown variable"])+--+-- >>> parseMsg "f:o: error: f:o:2: 'a' : unknown variable"+-- ([],["f:o:2: 'a' : unknown variable"])+--+-- >>> parseMsg "f:o: error: f:o:2: 'return' : type does not match, or is not convertible to, the function's return type"+-- ([],["f:o:2: 'return' : type does not match, or is not convertible to, the function's return type"])+--+-- >>> parseMsg "foo: foo(1): error at column 3, HLSL parsing failed."+-- ([],["foo:1: error at column 3, HLSL parsing failed."])+parseMsg :: String -> ([ShadercWarning], [ShadercError])+parseMsg = runParser $ foldl1+ (<++)+ [ do+ f <- filename+ line <- between colon colon number+ skipSpaces+ t <- msgType+ msg <- manyTill get eof+ pure $ formatMsg t f line msg+ , do+ f <- filename+ colon *> skipSpaces+ t <- msgType+ _ <- string f+ line <- between (char ':') (char ':') number+ skipSpaces+ msg <- manyTill get eof+ pure $ formatMsg t f line msg+ , do+ f <- filename+ colon *> skipSpaces+ _ <- string f+ line <- between (char '(') (char ')') number+ colon *> skipSpaces+ let t x = ([], [x])+ msg <- manyTill get eof+ pure $ formatMsg t f line msg+ , do+ _ <- number+ skipSpaces+ _ <- string "errors generated"+ eof+ pure ([], [])+ , do+ -- Unknown format+ msg <- manyTill get eof+ eof+ pure ([], [msg])+ ]+ where+ formatMsg t f line msg = t (f <> ":" <> show line <> ": " <> msg)+ filename = many1 get+ number = readS_to_P (reads @Integer)+ colon = void $ char ':'+ msgType =+ asum+ [ (\x -> ([], [x])) <$ string "error"+ , (\x -> ([x], [])) <$ string "warning"+ ]+ <* colon+ <* skipSpaces++runParser :: Monoid p => ReadP p -> String -> p+runParser p s = case readP_to_S p s of+ [(r, "")] -> r+ _ -> mempty
+ src/Vulkan/Utils/ShaderQQ/Backend/Shaderc/Internal.hs view
@@ -0,0 +1,87 @@+module Vulkan.Utils.ShaderQQ.Backend.Shaderc.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.Shaderc+import Vulkan.Utils.ShaderQQ.Backend.Internal++-- * Utilities++-- | Compile a GLSL/HLSL shader to SPIR-V using glslc (from the shaderc project)+--+-- Messages are converted to GHC warnings or errors depending on compilation success.+compileShaderQ+ :: Maybe String+ -- ^ Argument to pass to `--target-spv`+ -> ShaderType+ -- ^ Argument to specify between glsl/hlsl shader+ -> String+ -- ^ stage+ -> Maybe String+ -- ^ Argument to specify entry-point function name for hlsl+ -> String+ -- ^ glsl or hlsl shader code+ -> Q Exp+ -- ^ Spir-V bytecode+compileShaderQ targetSpv shaderType stage entryPoint code = do+ loc <- location+ (warnings, result) <- compileShader (Just loc) targetSpv shaderType stage entryPoint code+ bs <- messageProcess "glslc" reportWarning fail (warnings, result)+ bsToExp bs++-- | Compile a GLSL/HLSL shader to spir-v using glslc+compileShader+ :: MonadIO m+ => Maybe Loc+ -- ^ Source location+ -> Maybe String+ -- ^ Argument to pass to `--target-spv`+ -> ShaderType+ -- ^ Argument to specify between glsl/hlsl shader+ -> String+ -- ^ stage+ -> Maybe String+ -- ^ Argument to specify entry-point function name for hlsl+ -> String+ -- ^ glsl or hlsl shader code+ -> m ([ShadercWarning], Either [ShadercError] ByteString)+ -- ^ Spir-V bytecode with warnings or errors+compileShader loc targetSpv 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 targetSpv of+ Nothing -> []+ Just t -> ["--target-spv=" <> t]+ -- https://github.com/google/shaderc/blob/01dd72d6079ebdc0f96859365ba7abb1b62758bf/glslc/src/main.cc#L64+ entryPointArgs = case entryPoint of+ Nothing -> []+ Just name -> case shaderType of+ GLSL -> []+ HLSL -> ["-fentry-point=" <> name] + args = targetArgs ++ entryPointArgs ++ ["-fshader-stage=" <> stage, "-x", show shaderType, shader, "-o", spirv]+ (rc, out, err) <- readProcess $ proc "glslc" args+ let (warnings, errors) = processShadercMessages (out <> err)+ case rc of+ ExitSuccess -> do+ bs <- BS.readFile spirv+ pure (warnings, Right bs)+ ExitFailure _rc -> pure (warnings, Left errors)
+ src/Vulkan/Utils/ShaderQQ/GLSL.hs view
@@ -0,0 +1,59 @@+module Vulkan.Utils.ShaderQQ.GLSL+ ( glsl+ , insertLineDirective+ ) where++import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Vulkan.Utils.Internal ( badQQ )+import Vulkan.Utils.ShaderQQ.Interpolate+import Data.Char+import Data.List.Extra++-- $setup+-- >>> :set -XQuasiQuotes++-- | 'glsl' is a QuasiQuoter which produces GLSL source code with @#line@+-- directives inserted so that error locations point to the correct location in+-- the Haskell source file. It also permits basic string interpolation.+--+-- - Interpolated variables are prefixed with @$@+-- - They can optionally be surrounded with braces like @${foo}@+-- - Interpolated variables are converted to strings with 'show'+-- - To escape a @$@ use @\\$@+--+-- An explicit example (@<interactive>@ is from doctest):+--+-- >>> let version = 450 :: Int in [glsl|#version $version|]+-- "#version 450\n#extension GL_GOOGLE_cpp_style_line_directive : enable\n#line 72 \"<interactive>\"\n"+--+-- Note that line number will be thrown off if any of the interpolated+-- variables contain newlines.+glsl :: QuasiQuoter+glsl = (badQQ "glsl")+ { quoteExp = \s -> do+ loc <- location+ -- Insert the directive here, `compileShaderQ` will insert+ -- another one, but it's before this one, so who cares.+ let codeWithLineDirective = insertLineDirective s loc+ interpExp codeWithLineDirective+ }++-- If possible, insert a #line directive after the #version directive (as well+-- as the extension which allows filenames in line directives.+insertLineDirective :: String -> Loc -> String+insertLineDirective code Loc {..} =+ let isVersionDirective = ("#version" `isPrefixOf`) . dropWhile isSpace+ codeLines = lines code+ (beforeVersion, afterVersion) = break isVersionDirective codeLines+ lineDirective =+ [ "#extension GL_GOOGLE_cpp_style_line_directive : enable"+ , "#line "+ <> show (fst loc_start + length beforeVersion + 1)+ <> " \""+ <> loc_filename+ <> "\""+ ]+ in case afterVersion of+ [] -> code+ v : xs -> unlines $ beforeVersion <> [v] <> lineDirective <> xs
+ src/Vulkan/Utils/ShaderQQ/GLSL/Glslang.hs view
@@ -0,0 +1,202 @@+module Vulkan.Utils.ShaderQQ.GLSL.Glslang+ ( glsl+ , comp+ , frag+ , geom+ , tesc+ , tese+ , vert+ , rgen+ , rint+ , rahit+ , rchit+ , rmiss+ , rcall+ , task+ , mesh+ , compileShaderQ+ , compileShader+ ) where++import Control.Monad.IO.Class+import Data.ByteString ( ByteString )+import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Vulkan.Utils.Internal ( badQQ )+import Vulkan.Utils.ShaderQQ.ShaderType+import Vulkan.Utils.ShaderQQ.Backend.Glslang ( GlslangError, GlslangWarning )+import qualified Vulkan.Utils.ShaderQQ.Backend.Glslang.Internal as Glslang+import qualified Vulkan.Utils.ShaderQQ.GLSL as GLSL++-- $setup+-- >>> :set -XQuasiQuotes++-- | 'glsl' is a QuasiQuoter which produces GLSL source code with @#line@+-- directives inserted so that error locations point to the correct location in+-- the Haskell source file. It also permits basic string interpolation.+--+-- - Interpolated variables are prefixed with @$@+-- - They can optionally be surrounded with braces like @${foo}@+-- - Interpolated variables are converted to strings with 'show'+-- - To escape a @$@ use @\\$@+--+-- It is intended to be used in concert with 'compileShaderQ' like so+--+-- @+-- myConstant = 3.141 -- Note that this will have to be in a different module+-- myFragmentShader = $(compileShaderQ Nothing "frag" Nothing [glsl|+-- #version 450+-- const float myConstant = ${myConstant};+-- main (){+-- }+-- |])+-- @+--+-- An explicit example (@<interactive>@ is from doctest):+--+-- >>> let version = 450 :: Int in [glsl|#version $version|]+-- "#version 450\n#extension GL_GOOGLE_cpp_style_line_directive : enable\n#line 32 \"<interactive>\"\n"+--+-- Note that line number will be thrown off if any of the interpolated+-- variables contain newlines.+glsl :: QuasiQuoter+glsl = GLSL.glsl++-- | QuasiQuoter for creating a compute shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "comp" Nothing [glsl|...|])@ without+-- interpolation support.+comp :: QuasiQuoter+comp = shaderQQ "comp"++-- | QuasiQuoter for creating a fragment shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "frag" Nothing [glsl|...|])@ without+-- interpolation support.+frag :: QuasiQuoter+frag = shaderQQ "frag"++-- | QuasiQuoter for creating a geometry shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "geom" Nothing [glsl|...|])@ without+-- interpolation support.+geom :: QuasiQuoter+geom = shaderQQ "geom"++-- | QuasiQuoter for creating a tessellation control shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "tesc" Nothing [glsl|...|])@ without+-- interpolation support.+tesc :: QuasiQuoter+tesc = shaderQQ "tesc"++-- | QuasiQuoter for creating a tessellation evaluation shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "tese" Nothing [glsl|...|])@ without+-- interpolation support.+tese :: QuasiQuoter+tese = shaderQQ "tese"++-- | QuasiQuoter for creating a vertex shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "vert" Nothing [glsl|...|])@ without+-- interpolation support.+vert :: QuasiQuoter+vert = shaderQQ "vert"++-- | QuasiQuoter for creating a ray generation shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rgen" Nothing [glsl|...|])@ without+-- interpolation support.+rgen :: QuasiQuoter+rgen = rayShaderQQ "rgen"++-- | QuasiQuoter for creating an intersection shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rint" Nothing [glsl|...|])@ without+-- interpolation support.+rint :: QuasiQuoter+rint = rayShaderQQ "rint"++-- | QuasiQuoter for creating an any-hit shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rahit" Nothing [glsl|...|])@ without+-- interpolation support.+rahit :: QuasiQuoter+rahit = rayShaderQQ "rahit"++-- | QuasiQuoter for creating a closest hit shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rchit" Nothing [glsl|...|])@ without+-- interpolation support.+rchit :: QuasiQuoter+rchit = rayShaderQQ "rchit"++-- | QuasiQuoter for creating a miss shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rmiss" Nothing [glsl|...|])@ without+-- interpolation support.+rmiss :: QuasiQuoter+rmiss = rayShaderQQ "rmiss"++-- | QuasiQuoter for creating a callable shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rcall" Nothing [glsl|...|])@ without+-- interpolation support.+rcall :: QuasiQuoter+rcall = rayShaderQQ "rcall"++-- | QuasiQuoter for creating a task shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "task" Nothing [glsl|...|])@ without+-- interpolation support.+task :: QuasiQuoter+task = shaderQQ "task"++-- | QuasiQuoter for creating a mesh shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "mesh" Nothing [glsl|...|])@ without+-- interpolation support.+mesh :: QuasiQuoter+mesh = shaderQQ "mesh"++shaderQQ :: String -> QuasiQuoter+shaderQQ stage = (badQQ stage) { quoteExp = compileShaderQ Nothing stage Nothing }++rayShaderQQ :: String -> QuasiQuoter+rayShaderQQ stage = (badQQ stage) { quoteExp = compileShaderQ (Just "spirv1.4") stage Nothing }++-- * Utilities++-- | Compile a GLSL 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`+ -> String+ -- ^ stage+ -> Maybe String+ -- ^ Argument name to pass to `-e name --source-entry-point main` to specify entry-point function name+ -> String+ -- ^ glsl shader code+ -> Q Exp+ -- ^ Spir-V bytecode+compileShaderQ targetEnv = Glslang.compileShaderQ targetEnv GLSL++-- | Compile a GLSL shader to spir-v using glslangValidator.+compileShader+ :: MonadIO m+ => Maybe Loc+ -- ^ Source location+ -> Maybe String+ -- ^ Argument to pass to `--target-env`+ -> String+ -- ^ stage+ -> Maybe String+ -- ^ Argument name to pass to `-e name --source-entry-point main` to specify entry-point function name+ -> String+ -- ^ glsl shader code+ -> m ([GlslangWarning], Either [GlslangError] ByteString)+ -- ^ Spir-V bytecode with warnings or errors+compileShader loc targetEnv = Glslang.compileShader loc targetEnv GLSL
+ src/Vulkan/Utils/ShaderQQ/GLSL/Shaderc.hs view
@@ -0,0 +1,198 @@+module Vulkan.Utils.ShaderQQ.GLSL.Shaderc+ ( glsl+ , comp+ , frag+ , geom+ , tesc+ , tese+ , vert+ , rgen+ , rint+ , rahit+ , rchit+ , rmiss+ , rcall+ , task+ , mesh+ , compileShaderQ+ , compileShader+ ) where++import Control.Monad.IO.Class+import Data.ByteString ( ByteString )+import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Vulkan.Utils.Internal ( badQQ )+import Vulkan.Utils.ShaderQQ.ShaderType+import Vulkan.Utils.ShaderQQ.Backend.Shaderc ( ShadercError, ShadercWarning )+import qualified Vulkan.Utils.ShaderQQ.Backend.Shaderc.Internal as Shaderc+import qualified Vulkan.Utils.ShaderQQ.GLSL as GLSL++-- $setup+-- >>> :set -XQuasiQuotes++-- | 'glsl' is a QuasiQuoter which produces GLSL source code with @#line@+-- directives inserted so that error locations point to the correct location in+-- the Haskell source file. It also permits basic string interpolation.+--+-- - Interpolated variables are prefixed with @$@+-- - They can optionally be surrounded with braces like @${foo}@+-- - Interpolated variables are converted to strings with 'show'+-- - To escape a @$@ use @\\$@+--+-- It is intended to be used in concert with 'compileShaderQ' like so+--+-- @+-- myConstant = 3.141 -- Note that this will have to be in a different module+-- myFragmentShader = $(compileShaderQ Nothing "frag" [glsl|+-- #version 450+-- const float myConstant = ${myConstant};+-- main (){+-- }+-- |])+-- @+--+-- An explicit example (@<interactive>@ is from doctest):+--+-- >>> let version = 450 :: Int in [glsl|#version $version|]+-- "#version 450\n#extension GL_GOOGLE_cpp_style_line_directive : enable\n#line 52 \"<interactive>\"\n"+--+-- Note that line number will be thrown off if any of the interpolated+-- variables contain newlines.+glsl :: QuasiQuoter+glsl = GLSL.glsl++-- | QuasiQuoter for creating a compute shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "comp" [glsl|...|])@ without+-- interpolation support.+comp :: QuasiQuoter+comp = shaderQQ "comp"++-- | QuasiQuoter for creating a fragment shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "frag" [glsl|...|])@ without+-- interpolation support.+frag :: QuasiQuoter+frag = shaderQQ "frag"++-- | QuasiQuoter for creating a geometry shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "geom" [glsl|...|])@ without+-- interpolation support.+geom :: QuasiQuoter+geom = shaderQQ "geom"++-- | QuasiQuoter for creating a tessellation control shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "tesc" [glsl|...|])@ without+-- interpolation support.+tesc :: QuasiQuoter+tesc = shaderQQ "tesc"++-- | QuasiQuoter for creating a tessellation evaluation shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "tese" [glsl|...|])@ without+-- interpolation support.+tese :: QuasiQuoter+tese = shaderQQ "tese"++-- | QuasiQuoter for creating a vertex shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "vert" [glsl|...|])@ without+-- interpolation support.+vert :: QuasiQuoter+vert = shaderQQ "vert"++-- | QuasiQuoter for creating a ray generation shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rgen" [glsl|...|])@ without+-- interpolation support.+rgen :: QuasiQuoter+rgen = rayShaderQQ "rgen"++-- | QuasiQuoter for creating an intersection shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rint" [glsl|...|])@ without+-- interpolation support.+rint :: QuasiQuoter+rint = rayShaderQQ "rint"++-- | QuasiQuoter for creating an any-hit shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rahit" [glsl|...|])@ without+-- interpolation support.+rahit :: QuasiQuoter+rahit = rayShaderQQ "rahit"++-- | QuasiQuoter for creating a closest hit shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rchit" [glsl|...|])@ without+-- interpolation support.+rchit :: QuasiQuoter+rchit = rayShaderQQ "rchit"++-- | QuasiQuoter for creating a miss shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rmiss" [glsl|...|])@ without+-- interpolation support.+rmiss :: QuasiQuoter+rmiss = rayShaderQQ "rmiss"++-- | QuasiQuoter for creating a callable shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rcall" [glsl|...|])@ without+-- interpolation support.+rcall :: QuasiQuoter+rcall = rayShaderQQ "rcall"++-- | QuasiQuoter for creating a task shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "task" [glsl|...|])@ without+-- interpolation support.+task :: QuasiQuoter+task = shaderQQ "task"++-- | QuasiQuoter for creating a mesh shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "mesh" [glsl|...|])@ without+-- interpolation support.+mesh :: QuasiQuoter+mesh = shaderQQ "mesh"++shaderQQ :: String -> QuasiQuoter+shaderQQ stage = (badQQ stage) { quoteExp = compileShaderQ Nothing stage }++rayShaderQQ :: String -> QuasiQuoter+rayShaderQQ stage = (badQQ stage) { quoteExp = compileShaderQ (Just "spv1.4") stage }++-- * Utilities++-- | Compile a GLSL shader to spir-v using glslc.+--+-- Messages are converted to GHC warnings or errors depending on compilation success.+compileShaderQ+ :: Maybe String+ -- ^ Argument to pass to `--target-env`+ -> String+ -- ^ stage+ -> String+ -- ^ glsl shader code+ -> Q Exp+ -- ^ Spir-V bytecode+compileShaderQ targetEnv stage = Shaderc.compileShaderQ targetEnv GLSL stage Nothing++-- | Compile a GLSL shader to spir-v using glslc.+compileShader+ :: MonadIO m+ => Maybe Loc+ -- ^ Source location+ -> Maybe String+ -- ^ Argument to pass to `--target-env`+ -> String+ -- ^ stage+ -> String+ -- ^ glsl shader code+ -> m ([ShadercWarning], Either [ShadercError] ByteString)+ -- ^ Spir-V bytecode with warnings or errors+compileShader loc targetEnv stage = Shaderc.compileShader loc targetEnv GLSL stage Nothing
+ src/Vulkan/Utils/ShaderQQ/HLSL.hs view
@@ -0,0 +1,42 @@+module Vulkan.Utils.ShaderQQ.HLSL+ ( hlsl+ , insertLineDirective+ ) where++import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Vulkan.Utils.Internal ( badQQ )+import Vulkan.Utils.ShaderQQ.Interpolate++-- | 'hlsl' is a QuasiQuoter which produces HLSL source code with a @#line@+-- directive inserted so that error locations point to the correct location in+-- the Haskell source file. It also permits basic string interpolation.+--+-- - Interpolated variables are prefixed with @$@+-- - They can optionally be surrounded with braces like @${foo}@+-- - Interpolated variables are converted to strings with 'show'+-- - To escape a @$@ use @\\$@+--+-- An explicit example (@<interactive>@ is from doctest):+--+-- >>> let foo = 450 :: Int in [hlsl|const float foo = $foo|]+-- "#line 77 \"<interactive>\"\nconst float foo = 450"+--+-- Note that line number will be thrown off if any of the interpolated+-- variables contain newlines.+hlsl :: QuasiQuoter+hlsl = (badQQ "hlsl")+ { quoteExp = \s -> do+ loc <- location+ -- Insert the directive here, `compileShaderQ` will insert+ -- another one, but it's before this one, so who cares.+ let codeWithLineDirective = insertLineDirective s loc+ interpExp codeWithLineDirective+ }++-- Insert a #line directive with the specified location at the top of the file+insertLineDirective :: String -> Loc -> String+insertLineDirective code Loc {..} =+ let lineDirective =+ "#line " <> show (fst loc_start) <> " \"" <> loc_filename <> "\""+ in lineDirective <> "\n" <> code
+ src/Vulkan/Utils/ShaderQQ/HLSL/Glslang.hs view
@@ -0,0 +1,199 @@+module Vulkan.Utils.ShaderQQ.HLSL.Glslang+ ( hlsl+ , comp+ , frag+ , geom+ , tesc+ , tese+ , vert+ , rgen+ , rint+ , rahit+ , rchit+ , rmiss+ , rcall+ , task+ , mesh+ , compileShaderQ+ , compileShader+ ) where++import Control.Monad.IO.Class+import Data.ByteString ( ByteString )+import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Vulkan.Utils.Internal ( badQQ )+import Vulkan.Utils.ShaderQQ.ShaderType+import Vulkan.Utils.ShaderQQ.Backend.Glslang ( GlslangError, GlslangWarning )+import qualified Vulkan.Utils.ShaderQQ.Backend.Glslang.Internal as Glslang+import qualified Vulkan.Utils.ShaderQQ.HLSL as HLSL++-- | 'hlsl' is a QuasiQuoter which produces HLSL source code with a @#line@+-- directive inserted so that error locations point to the correct location in+-- the Haskell source file. It also permits basic string interpolation.+--+-- - Interpolated variables are prefixed with @$@+-- - They can optionally be surrounded with braces like @${foo}@+-- - Interpolated variables are converted to strings with 'show'+-- - To escape a @$@ use @\\$@+--+-- It is intended to be used in concert with 'compileShaderQ' like so+--+-- @+-- myConstant = 3.141 -- Note that this will have to be in a different module+-- myFragmentShader = $(compileShaderQ Nothing "frag" (Just "main") [hlsl|+-- static const float myConstant = ${myConstant};+-- float main (){+-- return myConstant;+-- }+-- |])+-- @+--+-- An explicit example (@<interactive>@ is from doctest):+--+-- >>> let foo = 450 :: Int in [hlsl|const float foo = $foo|]+-- "#line 37 \"<interactive>\"\nconst float foo = 450"+--+-- Note that line number will be thrown off if any of the interpolated+-- variables contain newlines.+hlsl :: QuasiQuoter+hlsl = HLSL.hlsl++-- | QuasiQuoter for creating a compute shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "comp" (Just "main") [hlsl|...|])@ without+-- interpolation support.+comp :: QuasiQuoter+comp = shaderQQ "comp"++-- | QuasiQuoter for creating a fragment shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "frag" (Just "main") [hlsl|...|])@ without+-- interpolation support.+frag :: QuasiQuoter+frag = shaderQQ "frag"++-- | QuasiQuoter for creating a geometry shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "geom" (Just "main") [hlsl|...|])@ without+-- interpolation support.+geom :: QuasiQuoter+geom = shaderQQ "geom"++-- | QuasiQuoter for creating a tessellation control shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "tesc" (Just "main") [hlsl|...|])@ without+-- interpolation support.+tesc :: QuasiQuoter+tesc = shaderQQ "tesc"++-- | QuasiQuoter for creating a tessellation evaluation shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "tese" (Just "main") [hlsl|...|])@ without+-- interpolation support.+tese :: QuasiQuoter+tese = shaderQQ "tese"++-- | QuasiQuoter for creating a vertex shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "vert" (Just "main") [hlsl|...|])@ without+-- interpolation support.+vert :: QuasiQuoter+vert = shaderQQ "vert"++-- | QuasiQuoter for creating a ray generation shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rgen" (Just "main") [hlsl|...|])@ without+-- interpolation support.+rgen :: QuasiQuoter+rgen = rayShaderQQ "rgen"++-- | QuasiQuoter for creating an intersection shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rint" (Just "main") [hlsl|...|])@ without+-- interpolation support.+rint :: QuasiQuoter+rint = rayShaderQQ "rint"++-- | QuasiQuoter for creating an any-hit shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rahit" (Just "main") [hlsl|...|])@ without+-- interpolation support.+rahit :: QuasiQuoter+rahit = rayShaderQQ "rahit"++-- | QuasiQuoter for creating a closest hit shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rchit" (Just "main") [hlsl|...|])@ without+-- interpolation support.+rchit :: QuasiQuoter+rchit = rayShaderQQ "rchit"++-- | QuasiQuoter for creating a miss shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rmiss" (Just "main") [hlsl|...|])@ without+-- interpolation support.+rmiss :: QuasiQuoter+rmiss = rayShaderQQ "rmiss"++-- | QuasiQuoter for creating a callable shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spirv1.4") "rcall" (Just "main") [hlsl|...|])@ without+-- interpolation support.+rcall :: QuasiQuoter+rcall = rayShaderQQ "rcall"++-- | QuasiQuoter for creating a task shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "task" (Just "main") [hlsl|...|])@ without+-- interpolation support.+task :: QuasiQuoter+task = shaderQQ "task"++-- | QuasiQuoter for creating a mesh shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "mesh" (Just "main") [hlsl|...|])@ without+-- interpolation support.+mesh :: QuasiQuoter+mesh = shaderQQ "mesh"++shaderQQ :: String -> QuasiQuoter+shaderQQ stage = (badQQ stage) { quoteExp = compileShaderQ Nothing stage (Just "main") }++rayShaderQQ :: String -> QuasiQuoter+rayShaderQQ stage = (badQQ stage) { quoteExp = compileShaderQ (Just "spirv1.4") stage (Just "main") }++-- * Utilities++-- | Compile a 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`+ -> String+ -- ^ stage+ -> Maybe String+ -- ^ Argument name to pass to `-e name` to specify entry-point function name+ -> String+ -- ^ hlsl shader code+ -> Q Exp+ -- ^ Spir-V bytecode+compileShaderQ targetEnv = Glslang.compileShaderQ targetEnv HLSL++-- | Compile a HLSL shader to spir-v using glslangValidator.+compileShader+ :: MonadIO m+ => Maybe Loc+ -- ^ Source location+ -> Maybe String+ -- ^ Argument to pass to `--target-env`+ -> String+ -- ^ stage+ -> Maybe String+ -- ^ Argument name to pass to `-e name` to specify entry-point function name+ -> String+ -- ^ hlsl shader code+ -> m ([GlslangWarning], Either [GlslangError] ByteString)+ -- ^ Spir-V bytecode with warnings or errors+compileShader loc targetEnv = Glslang.compileShader loc targetEnv HLSL
+ src/Vulkan/Utils/ShaderQQ/HLSL/Shaderc.hs view
@@ -0,0 +1,199 @@+module Vulkan.Utils.ShaderQQ.HLSL.Shaderc+ ( hlsl+ , comp+ , frag+ , geom+ , tesc+ , tese+ , vert+ , rgen+ , rint+ , rahit+ , rchit+ , rmiss+ , rcall+ , task+ , mesh+ , compileShaderQ+ , compileShader+ ) where++import Control.Monad.IO.Class+import Data.ByteString ( ByteString )+import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Vulkan.Utils.Internal ( badQQ )+import Vulkan.Utils.ShaderQQ.ShaderType+import Vulkan.Utils.ShaderQQ.Backend.Shaderc ( ShadercError, ShadercWarning )+import qualified Vulkan.Utils.ShaderQQ.Backend.Shaderc.Internal as Shaderc+import qualified Vulkan.Utils.ShaderQQ.HLSL as HLSL++-- | 'hlsl' is a QuasiQuoter which produces HLSL source code with a @#line@+-- directive inserted so that error locations point to the correct location in+-- the Haskell source file. It also permits basic string interpolation.+--+-- - Interpolated variables are prefixed with @$@+-- - They can optionally be surrounded with braces like @${foo}@+-- - Interpolated variables are converted to strings with 'show'+-- - To escape a @$@ use @\\$@+--+-- It is intended to be used in concert with 'compileShaderQ' like so+--+-- @+-- myConstant = 3.141 -- Note that this will have to be in a different module+-- myFragmentShader = $(compileShaderQ Nothing "frag" Nothing [hlsl|+-- static const float myConstant = ${myConstant};+-- float main (){+-- return myConstant;+-- }+-- |])+-- @+--+-- An explicit example (@<interactive>@ is from doctest):+--+-- >>> let foo = 450 :: Int in [hlsl|const float foo = $foo|]+-- "#line 57 \"<interactive>\"\nconst float foo = 450"+--+-- Note that line number will be thrown off if any of the interpolated+-- variables contain newlines.+hlsl :: QuasiQuoter+hlsl = HLSL.hlsl++-- | QuasiQuoter for creating a compute shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "comp" Nothing [hlsl|...|])@ without+-- interpolation support.+comp :: QuasiQuoter+comp = shaderQQ "comp"++-- | QuasiQuoter for creating a fragment shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "frag" Nothing [hlsl|...|])@ without+-- interpolation support.+frag :: QuasiQuoter+frag = shaderQQ "frag"++-- | QuasiQuoter for creating a geometry shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "geom" Nothing [hlsl|...|])@ without+-- interpolation support.+geom :: QuasiQuoter+geom = shaderQQ "geom"++-- | QuasiQuoter for creating a tessellation control shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "tesc" Nothing [hlsl|...|])@ without+-- interpolation support.+tesc :: QuasiQuoter+tesc = shaderQQ "tesc"++-- | QuasiQuoter for creating a tessellation evaluation shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "tese" Nothing [hlsl|...|])@ without+-- interpolation support.+tese :: QuasiQuoter+tese = shaderQQ "tese"++-- | QuasiQuoter for creating a vertex shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "vert" Nothing [hlsl|...|])@ without+-- interpolation support.+vert :: QuasiQuoter+vert = shaderQQ "vert"++-- | QuasiQuoter for creating a ray generation shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rgen" Nothing [hlsl|...|])@ without+-- interpolation support.+rgen :: QuasiQuoter+rgen = rayShaderQQ "rgen"++-- | QuasiQuoter for creating an intersection shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rint" Nothing [hlsl|...|])@ without+-- interpolation support.+rint :: QuasiQuoter+rint = rayShaderQQ "rint"++-- | QuasiQuoter for creating an any-hit shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rahit" Nothing [hlsl|...|])@ without+-- interpolation support.+rahit :: QuasiQuoter+rahit = rayShaderQQ "rahit"++-- | QuasiQuoter for creating a closest hit shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rchit" Nothing [hlsl|...|])@ without+-- interpolation support.+rchit :: QuasiQuoter+rchit = rayShaderQQ "rchit"++-- | QuasiQuoter for creating a miss shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rmiss" Nothing [hlsl|...|])@ without+-- interpolation support.+rmiss :: QuasiQuoter+rmiss = rayShaderQQ "rmiss"++-- | QuasiQuoter for creating a callable shader.+--+-- Equivalent to calling @$(compileShaderQ (Just "spv1.4") "rcall" Nothing [hlsl|...|])@ without+-- interpolation support.+rcall :: QuasiQuoter+rcall = rayShaderQQ "rcall"++-- | QuasiQuoter for creating a task shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "task" Nothing [hlsl|...|])@ without+-- interpolation support.+task :: QuasiQuoter+task = shaderQQ "task"++-- | QuasiQuoter for creating a mesh shader.+--+-- Equivalent to calling @$(compileShaderQ Nothing "mesh" Nothing [hlsl|...|])@ without+-- interpolation support.+mesh :: QuasiQuoter+mesh = shaderQQ "mesh"++shaderQQ :: String -> QuasiQuoter+shaderQQ stage = (badQQ stage) { quoteExp = compileShaderQ Nothing stage Nothing }++rayShaderQQ :: String -> QuasiQuoter+rayShaderQQ stage = (badQQ stage) { quoteExp = compileShaderQ (Just "spv1.4") stage Nothing }++-- * Utilities++-- | Compile a HLSL shader to spir-v using glslc.+--+-- Messages are converted to GHC warnings or errors depending on compilation success.+compileShaderQ+ :: Maybe String+ -- ^ Argument to pass to `--target-env`+ -> String+ -- ^ stage+ -> Maybe String+ -- ^ Argument to pass to `-fentry-point=` to specify entry-point function name+ -> String+ -- ^ hlsl shader code+ -> Q Exp+ -- ^ Spir-V bytecode+compileShaderQ targetEnv = Shaderc.compileShaderQ targetEnv HLSL++-- | Compile a HLSL shader to spir-v using glslc.+compileShader+ :: MonadIO m+ => Maybe Loc+ -- ^ Source location+ -> Maybe String+ -- ^ Argument to pass to `--target-env`+ -> String+ -- ^ stage+ -> Maybe String+ -- ^ Argument to pass to `-fentry-point=` to specify entry-point function name+ -> String+ -- ^ hlsl shader code+ -> m ([ShadercWarning], Either [ShadercError] ByteString)+ -- ^ Spir-V bytecode with warnings or errors+compileShader loc targetEnv = Shaderc.compileShader loc targetEnv HLSL
+ src/Vulkan/Utils/ShaderQQ/ShaderType.hs view
@@ -0,0 +1,20 @@+module Vulkan.Utils.ShaderQQ.ShaderType+ ( ShaderType (..)+ ) where++import Data.String (IsString (..))++data ShaderType+ = GLSL+ | HLSL++instance IsString ShaderType where+ fromString = \case+ "glsl" -> GLSL+ "hlsl" -> HLSL+ t -> error $ "not support '" ++ t ++ "' shader"++instance Show ShaderType where+ show = \case+ GLSL -> "glsl"+ HLSL -> "hlsl"
− src/Vulkan/Utils/ShaderQQ/Shaderc.hs
@@ -1,285 +0,0 @@-module Vulkan.Utils.ShaderQQ.Shaderc- ( hlsl- , comp- , frag- , geom- , tesc- , tese- , vert- , ShadercError- , ShadercWarning- , compileShaderQ- , compileShader- , processShadercMessages- ) where--import Control.Monad ( void )-import Control.Monad.IO.Class-import Data.ByteString ( ByteString )-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy.Char8 as BSL-import Data.FileEmbed-import Data.Foldable ( asum )-import Data.List.Extra-import Language.Haskell.TH-import Language.Haskell.TH.Quote-import System.Exit-import System.IO.Temp-import System.Process.Typed-import Text.ParserCombinators.ReadP-import Vulkan.Utils.ShaderQQ.Interpolate---- $setup--- >>> :set -XQuasiQuotes---- | 'hlsl' is a QuasiQuoter which produces HLSL source code with a @#line@--- directive inserted so that error locations point to the correct location in--- the Haskell source file. It also permits basic string interpolation.------ - Interpolated variables are prefixed with @$@--- - They can optionally be surrounded with braces like @${foo}@--- - Interpolated variables are converted to strings with 'show'--- - To escape a @$@ use @\\$@------ It is intended to be used in concert with 'compileShaderQ' like so------ @--- myConstant = 3.141 -- Note that this will have to be in a different module--- myFragmentShader = $(compileShaderQ "frag" [hlsl|--- static const float myConstant = ${myConstant};--- float main (){--- return myConstant;--- }--- |])--- @------ An explicit example (@<interactive>@ is from doctest):------ >>> let foo = 450 :: Int in [hlsl|const float foo = $foo|]--- "#line 31 \"<interactive>\"\nconst float foo = 450"------ Note that line number will be thrown off if any of the interpolated--- variables contain newlines.-hlsl :: QuasiQuoter-hlsl = (badQQ "hlsl")- { quoteExp = \s -> do- loc <- location- -- Insert the directive here, `compileShaderQ` will insert- -- another one, but it's before this one, so who cares.- let codeWithLineDirective = insertLineDirective s loc- interpExp codeWithLineDirective- }---- | QuasiQuoter for creating a compute shader.------ Equivalent to calling @$(compileShaderQ "comp" [hlsl|...|])@ without--- interpolation support.-comp :: QuasiQuoter-comp = shaderQQ "comp"---- | QuasiQuoter for creating a fragment shader.------ Equivalent to calling @$(compileShaderQ "frag" [hlsl|...|])@ without--- interpolation support.-frag :: QuasiQuoter-frag = shaderQQ "frag"---- | QuasiQuoter for creating a geometry shader.------ Equivalent to calling @$(compileShaderQ "geom" [hlsl|...|])@ without--- interpolation support.-geom :: QuasiQuoter-geom = shaderQQ "geom"---- | QuasiQuoter for creating a tessellation control shader.------ Equivalent to calling @$(compileShaderQ "tesc" [hlsl|...|])@ without--- interpolation support.-tesc :: QuasiQuoter-tesc = shaderQQ "tesc"---- | QuasiQuoter for creating a tessellation evaluation shader.------ Equivalent to calling @$(compileShaderQ "tese" [hlsl|...|])@ without--- interpolation support.-tese :: QuasiQuoter-tese = shaderQQ "tese"---- | QuasiQuoter for creating a vertex shader.------ Equivalent to calling @$(compileShaderQ "vert" [hlsl|...|])@ without--- interpolation support.-vert :: QuasiQuoter-vert = shaderQQ "vert"--shaderQQ :: String -> QuasiQuoter-shaderQQ stage = (badQQ stage) { quoteExp = compileShaderQ stage }---- * Utilities---- | Compile a HLSL shader to SPIR-V using glslc (from the shaderc project)------ Messages are converted to GHC warnings or errors depending on compilation success.-compileShaderQ- :: String- -- ^ stage- -> String- -- ^ glsl or code- -> Q Exp- -- ^ Spir-V bytecode-compileShaderQ stage code = do- loc <- location- (warnings, result) <- compileShader (Just loc) stage code- case warnings of- [] -> pure ()- _some -> reportWarning $ prepare warnings-- bs <- case result of- Left [] -> fail "glslc failed with no errors"- Left errors -> do- reportError $ prepare errors- pure mempty- Right bs -> pure bs-- bsToExp bs-- where- prepare [singleLine] = singleLine- prepare multiline =- intercalate "\n" $ "glslc:" : map (mappend " ") multiline--type ShadercError = String-type ShadercWarning = String---- | Compile a HLSL shader to spir-v using glslc-compileShader- :: MonadIO m- => Maybe Loc- -- ^ Source location- -> String- -- ^ stage- -> String- -- ^ HLSL code- -> m ([ShadercWarning], Either [ShadercError] ByteString)- -- ^ Spir-V bytecode with warnings or errors-compileShader loc stage code =- liftIO $ withSystemTempDirectory "th-shader" $ \dir -> do- let codeWithLineDirective = maybe code (insertLineDirective code) loc- let shader = dir <> "/shader.hlsl"- spirv = dir <> "/shader.spv"- writeFile shader codeWithLineDirective-- (rc, out, err) <- readProcess $ proc- "glslc"- ["-fshader-stage=" <> stage, "-x", "hlsl", shader, "-o", spirv]- let (warnings, errors) = processShadercMessages (out <> err)- case rc of- ExitSuccess -> do- bs <- BS.readFile spirv- pure (warnings, Right bs)- ExitFailure _rc -> pure (warnings, Left errors)--processShadercMessages :: BSL.ByteString -> ([ShadercWarning], [ShadercError])-processShadercMessages = foldMap parseMsg . lines . BSL.unpack---- >>> parseMsg "blah"--- ([],[])------ >>> parseMsg "blah"--- ([],["blah"])------ >>> parseMsg "foo:2: error: unknown var"--- ([],["foo:2: unknown var"])------ >>> parseMsg "foo:2: warning: unknown var"--- (["foo:2: unknown var"],[])------ >>> parseMsg "bar:2: error: 'a' : unknown variable"--- ([],["bar:2: 'a' : unknown variable"])------ >>> parseMsg "f:o: error: f:o:2: 'a' : unknown variable"--- ([],["f:o:2: 'a' : unknown variable"])------ >>> parseMsg "f:o: error: f:o:2: 'return' : type does not match, or is not convertible to, the function's return type"--- ([],["f:o:2: 'return' : type does not match, or is not convertible to, the function's return type"])------ >>> parseMsg "foo: foo(1): error at column 3, HLSL parsing failed."--- ([],["foo:1: error at column 3, HLSL parsing failed."])-parseMsg :: String -> ([ShadercWarning], [ShadercError])-parseMsg = runParser $ foldl1- (<++)- [ do- f <- filename- line <- between colon colon number- skipSpaces- t <- msgType- msg <- manyTill get eof- pure $ formatMsg t f line msg- , do- f <- filename- colon *> skipSpaces- t <- msgType- _ <- string f- line <- between (char ':') (char ':') number- skipSpaces- msg <- manyTill get eof- pure $ formatMsg t f line msg- , do- f <- filename- colon *> skipSpaces- _ <- string f- line <- between (char '(') (char ')') number- colon *> skipSpaces- let t x = ([], [x])- msg <- manyTill get eof- pure $ formatMsg t f line msg- , do- _ <- number- skipSpaces- _ <- string "errors generated"- eof- pure ([], [])- , do- -- Unknown format- msg <- manyTill get eof- eof- pure ([], [msg])- ]- where- formatMsg t f line msg = t (f <> ":" <> show line <> ": " <> msg)- filename = many1 get- number = readS_to_P (reads @Integer)- colon = void $ char ':'- msgType =- asum- [ (\x -> ([], [x])) <$ string "error"- , (\x -> ([x], [])) <$ string "warning"- ]- <* colon- <* skipSpaces--runParser :: Monoid p => ReadP p -> String -> p-runParser p s = case readP_to_S p s of- [(r, "")] -> r- _ -> mempty---- Insert a #line directive with the specified location at the top of the file-insertLineDirective :: String -> Loc -> String-insertLineDirective code Loc {..} =- let lineDirective =- "#line " <> show (fst loc_start) <> " \"" <> loc_filename <> "\""- in lineDirective <> "\n" <> code--------------------------------------------------------------------- Utils-------------------------------------------------------------------badQQ :: String -> QuasiQuoter-badQQ name = QuasiQuoter (bad "expression")- (bad "pattern")- (bad "type")- (bad "declaration")- where- bad :: String -> a- bad context =- error $ "Can't use " <> name <> " quote in a " <> context <> " context"
vulkan-utils.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: vulkan-utils-version: 0.4.2+version: 0.5.0 synopsis: Utils for the vulkan package category: Graphics homepage: https://github.com/expipiplus1/vulkan#readme@@ -40,9 +40,19 @@ Vulkan.Utils.QueueAssignment Vulkan.Utils.Requirements Vulkan.Utils.Requirements.TH- Vulkan.Utils.ShaderQQ+ Vulkan.Utils.ShaderQQ.Backend.Glslang+ Vulkan.Utils.ShaderQQ.Backend.Glslang.Internal+ Vulkan.Utils.ShaderQQ.Backend.Internal+ Vulkan.Utils.ShaderQQ.Backend.Shaderc+ Vulkan.Utils.ShaderQQ.Backend.Shaderc.Internal+ Vulkan.Utils.ShaderQQ.GLSL+ Vulkan.Utils.ShaderQQ.GLSL.Glslang+ Vulkan.Utils.ShaderQQ.GLSL.Shaderc+ Vulkan.Utils.ShaderQQ.HLSL+ Vulkan.Utils.ShaderQQ.HLSL.Glslang+ Vulkan.Utils.ShaderQQ.HLSL.Shaderc Vulkan.Utils.ShaderQQ.Interpolate- Vulkan.Utils.ShaderQQ.Shaderc+ Vulkan.Utils.ShaderQQ.ShaderType other-modules: Vulkan.Utils.Internal hs-source-dirs: