{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} module GraphicsPipeline where import Linear import qualified Control.Concurrent.STM as STM import Control.Monad.Reader import Control.Monad.Trans.Resource import Data.Bits -- import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BS import qualified Data.Map.Strict as M import qualified Data.Vector as V import Foreign (sizeOf) import Foreign.C.Types (CInt) import qualified Vulkan as VK import qualified Vulkan.Core10 as Vk import qualified Vulkan.Zero as Vk import qualified Vulkan.CStruct.Extends as Vk import qualified Vulkan.Utils.ShaderQQ.GLSL.Shaderc as Vk -- internal imports import Types loadShader :: (MonadResource m) => Vk.Device -> FilePath -> String -> m Vk.ShaderModule loadShader logicalDevice shaderPath stage = do shaderSource <- liftIO $ BS.readFile shaderPath (warnings, eShader) <- Vk.compileShader Nothing Nothing stage (BS.unpack shaderSource) unless (null warnings) $ liftIO $ do putStrLn "loadShader: Shader compilation warnings:" print warnings shader <- either (\errors -> do error ("loadShader: Compilation errors:" <> "\n" <> show errors) ) return eShader let createInfo = Vk.zero { Vk.code = shader } snd <$> allocate (Vk.createShaderModule logicalDevice createInfo Nothing) (\shaderModule -> do putStrLn "destroying shader module" Vk.destroyShaderModule logicalDevice shaderModule Nothing ) createRenderPass :: (MonadResource m) => Vk.Device -> Vk.Format -> Vk.Format -> m Vk.RenderPass createRenderPass logicalDevice swapchainImageFormat depthFormat = do let colorAttachmentDescription = (Vk.zero :: Vk.AttachmentDescription) { Vk.format = swapchainImageFormat , Vk.samples = Vk.SAMPLE_COUNT_1_BIT , Vk.loadOp = Vk.ATTACHMENT_LOAD_OP_CLEAR , Vk.storeOp = Vk.ATTACHMENT_STORE_OP_STORE , Vk.stencilLoadOp = Vk.ATTACHMENT_LOAD_OP_DONT_CARE , Vk.stencilStoreOp = Vk.ATTACHMENT_STORE_OP_DONT_CARE , Vk.initialLayout = Vk.IMAGE_LAYOUT_UNDEFINED , Vk.finalLayout = Vk.IMAGE_LAYOUT_PRESENT_SRC_KHR } colorAttachmentReference = (Vk.zero :: Vk.AttachmentReference) { Vk.attachment = 0 , Vk.layout = Vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL } depthAttachmentDescription = (Vk.zero :: Vk.AttachmentDescription) { Vk.flags = bit 0 , Vk.format = depthFormat , Vk.samples = Vk.SAMPLE_COUNT_1_BIT , Vk.loadOp = Vk.ATTACHMENT_LOAD_OP_CLEAR , Vk.storeOp = Vk.ATTACHMENT_STORE_OP_STORE , Vk.stencilLoadOp = Vk.ATTACHMENT_LOAD_OP_CLEAR , Vk.stencilStoreOp = Vk.ATTACHMENT_STORE_OP_DONT_CARE , Vk.initialLayout = Vk.IMAGE_LAYOUT_UNDEFINED , Vk.finalLayout = Vk.IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL } depthAttachmentReference = (Vk.zero :: Vk.AttachmentReference) { Vk.attachment = 1 , Vk.layout = Vk.IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL } subpassDescriptor = (Vk.zero :: Vk.SubpassDescription) { Vk.pipelineBindPoint = Vk.PIPELINE_BIND_POINT_GRAPHICS , Vk.colorAttachments = V.singleton colorAttachmentReference , Vk.depthStencilAttachment = Just depthAttachmentReference } subpassDependency = Vk.zero { Vk.srcSubpass = Vk.SUBPASS_EXTERNAL , Vk.dstSubpass = 0 , Vk.srcStageMask = Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , Vk.dstStageMask = Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , Vk.srcAccessMask = Vk.ACCESS_MEMORY_READ_BIT , Vk.dstAccessMask = Vk.ACCESS_COLOR_ATTACHMENT_WRITE_BIT } subpassDepthDependency = Vk.zero { Vk.srcSubpass = Vk.SUBPASS_EXTERNAL , Vk.dstSubpass = 0 , Vk.srcStageMask = Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT .|. Vk.PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT , Vk.srcAccessMask = Vk.ACCESS_MEMORY_READ_BIT , Vk.dstStageMask = Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT .|. Vk.PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT , Vk.dstAccessMask = Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT } renderPassCreateInfo = Vk.zero { Vk.attachments = V.fromList [ colorAttachmentDescription, depthAttachmentDescription ] , Vk.subpasses = V.singleton subpassDescriptor , Vk.dependencies = V.fromList [ subpassDependency, subpassDepthDependency ] } :: Vk.RenderPassCreateInfo '[] snd <$> allocate (Vk.createRenderPass logicalDevice renderPassCreateInfo Nothing) (\renderPass -> do putStrLn "destroying render pass" Vk.destroyRenderPass logicalDevice renderPass Nothing ) createShaderStageCreateInfo :: Vk.ShaderStageFlagBits -> Vk.ShaderModule -> Vk.PipelineShaderStageCreateInfo '[] createShaderStageCreateInfo stageBit shaderModule = Vk.zero { Vk.stage = stageBit , Vk.module' = shaderModule , Vk.name = "main" } createVertexInputAssemblyStateCreateInfo :: Vk.PrimitiveTopology -> Vk.PipelineInputAssemblyStateCreateInfo createVertexInputAssemblyStateCreateInfo topology = Vk.zero { Vk.topology = topology , Vk.primitiveRestartEnable = False } createRasterizationStateCreateInfo :: Vk.PolygonMode -> Vk.SomeStruct Vk.PipelineRasterizationStateCreateInfo createRasterizationStateCreateInfo mode = Vk.SomeStruct $ Vk.zero { Vk.depthClampEnable = False , Vk.rasterizerDiscardEnable = False , Vk.polygonMode = mode , Vk.lineWidth = 1 , Vk.cullMode = Vk.CULL_MODE_NONE , Vk.frontFace = Vk.FRONT_FACE_CLOCKWISE , Vk.depthBiasEnable = False , Vk.depthBiasConstantFactor = 0 , Vk.depthBiasClamp = 0 , Vk.depthBiasSlopeFactor = 0 } createDepthStencilStateCreateInfo :: Bool -> Bool -> Vk.CompareOp -> Vk.PipelineDepthStencilStateCreateInfo createDepthStencilStateCreateInfo depthTest depthWrite compareOperation = Vk.zero { Vk.depthTestEnable = depthTest , Vk.depthWriteEnable = depthWrite , Vk.depthCompareOp = compareOperation , Vk.depthBoundsTestEnable = False , Vk.minDepthBounds = 0 , Vk.maxDepthBounds = 1 , Vk.stencilTestEnable = False } createMultisampleStateCreateInfo :: Vk.SomeStruct Vk.PipelineMultisampleStateCreateInfo createMultisampleStateCreateInfo = Vk.SomeStruct $ Vk.zero { Vk.sampleShadingEnable = False , Vk.rasterizationSamples = Vk.SAMPLE_COUNT_1_BIT } createGraphicsPipelines :: (MonadResource m, MonadReader ReadState m) => Vk.Device -> Vk.RenderPass -> ShaderContainer -> V2 CInt -> Vk.PipelineLayout -> Maybe Vk.PipelineDepthStencilStateCreateInfo -> m () createGraphicsPipelines logicalDevice renderPass shaderContainer (V2 width height) pipelineLayout depthState = do meshLib <- (liftIO . STM.atomically . STM.readTMVar) =<< asks meshLibrary let pipelineStagesCreateInfos = V.fromList $ map Vk.SomeStruct ( maybe [] (\shader -> [ createShaderStageCreateInfo Vk.SHADER_STAGE_VERTEX_BIT shader ] ) (containedVertexShader shaderContainer) ++ maybe [] (\shader -> [ createShaderStageCreateInfo Vk.SHADER_STAGE_FRAGMENT_BIT shader ] ) (containedFragmentShader shaderContainer) ) dynamicStates = V.fromList [ Vk.DYNAMIC_STATE_VIEWPORT , Vk.DYNAMIC_STATE_SCISSOR ] pipelineDynamicStateCreateInfo = Vk.zero { Vk.dynamicStates = dynamicStates } vertexDescription = getVertexDescription (V.head $ meshVertices $ meshLib M.! "mask") pipelineVertexInputCreateInfo = Vk.zero { Vk.vertexBindingDescriptions = vidBindings vertexDescription , Vk.vertexAttributeDescriptions = vidAttributes vertexDescription } viewport = Vk.Viewport { Vk.x = 0 , Vk.y = 0 , Vk.width = fromIntegral width , Vk.height = fromIntegral height , Vk.minDepth = 0 , Vk.maxDepth = 1 } scissor = Vk.Rect2D { Vk.offset = Vk.Offset2D 0 0 , Vk.extent = Vk.Extent2D (fromIntegral width) (fromIntegral height) } pipelineViewportStateCreateInfo = Vk.zero { Vk.viewports = V.singleton viewport , Vk.scissors = V.singleton scissor } colorBlendAttachment = Vk.zero { Vk.colorWriteMask = Vk.COLOR_COMPONENT_R_BIT .|. Vk.COLOR_COMPONENT_G_BIT .|. Vk.COLOR_COMPONENT_B_BIT .|. Vk.COLOR_COMPONENT_A_BIT , Vk.blendEnable = True , Vk.srcColorBlendFactor = Vk.BLEND_FACTOR_ONE , Vk.dstColorBlendFactor = Vk.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA , Vk.colorBlendOp = Vk.BLEND_OP_ADD , Vk.srcAlphaBlendFactor = Vk.BLEND_FACTOR_ONE , Vk.dstAlphaBlendFactor = Vk.BLEND_FACTOR_ZERO , Vk.alphaBlendOp = Vk.BLEND_OP_ADD } pipelineColorBlendStateCreateInfo = Vk.zero { Vk.logicOpEnable = False , Vk.attachments = V.singleton colorBlendAttachment } pipelineCreateInfo = Vk.zero { Vk.stageCount = fromIntegral (V.length pipelineStagesCreateInfos) , Vk.stages = pipelineStagesCreateInfos , Vk.vertexInputState = Just $ Vk.SomeStruct pipelineVertexInputCreateInfo , Vk.inputAssemblyState = Just $ createVertexInputAssemblyStateCreateInfo VK.PRIMITIVE_TOPOLOGY_TRIANGLE_LIST , Vk.viewportState = Just $ Vk.SomeStruct pipelineViewportStateCreateInfo , Vk.rasterizationState = Just $ createRasterizationStateCreateInfo Vk.POLYGON_MODE_FILL , Vk.multisampleState = Just createMultisampleStateCreateInfo , Vk.colorBlendState = Just $ Vk.SomeStruct pipelineColorBlendStateCreateInfo , Vk.dynamicState = Just pipelineDynamicStateCreateInfo , Vk.depthStencilState = depthState , Vk.layout = pipelineLayout , Vk.renderPass = renderPass , Vk.subpass = 0 , Vk.basePipelineHandle = Vk.NULL_HANDLE , Vk.basePipelineIndex = -1 } pipeline <- snd <$> allocate (do (result, pipelines) <- Vk.createGraphicsPipelines logicalDevice Vk.NULL_HANDLE (V.singleton (Vk.SomeStruct pipelineCreateInfo)) Nothing unless (result == Vk.SUCCESS) $ error "createGraphicsPipelines: Failed creating pipelines" return pipelines ) (\pipelines -> do V.mapM_ (\pipeline -> do putStrLn "destroying pipeline" Vk.destroyPipeline logicalDevice pipeline Nothing ) pipelines ) let material = Material (V.head pipeline) pipelineLayout matLibraryTMVar <- asks materialLibrary matLibrary <- liftIO $ STM.atomically $ STM.readTMVar matLibraryTMVar let newMatLibrary = M.insert "defaultMesh" material matLibrary void $ liftIO $ STM.atomically $ STM.swapTMVar matLibraryTMVar newMatLibrary createPipelineLayout :: MonadResource m => Vk.Device -> m Vk.PipelineLayout createPipelineLayout logicalDevice = do let pipelineLayoutCreateInfo = Vk.zero snd <$> allocate (Vk.createPipelineLayout logicalDevice pipelineLayoutCreateInfo Nothing) (\pipelineLayout -> do putStrLn "destroying pipeline layout" Vk.destroyPipelineLayout logicalDevice pipelineLayout Nothing ) createMeshPipelineLayout :: MonadResource m => Vk.Device -> m Vk.PipelineLayout createMeshPipelineLayout logicalDevice = do let pushConstantRange = Vk.zero { Vk.offset = 0 , Vk.size = fromIntegral (sizeOf (undefined :: MeshPushConstants)) , Vk.stageFlags = Vk.SHADER_STAGE_VERTEX_BIT } pipelineLayoutCreateInfo = Vk.zero { Vk.pushConstantRanges = V.singleton pushConstantRange } snd <$> allocate (Vk.createPipelineLayout logicalDevice pipelineLayoutCreateInfo Nothing) (\pipelineLayout -> do putStrLn "destroying pipeline layout" Vk.destroyPipelineLayout logicalDevice pipelineLayout Nothing )