summaryrefslogtreecommitdiff
path: root/VkTest
diff options
context:
space:
mode:
Diffstat (limited to 'VkTest')
-rw-r--r--VkTest/Config.hs248
-rw-r--r--VkTest/Shaders.hs43
-rw-r--r--VkTest/Util.hs162
3 files changed, 453 insertions, 0 deletions
diff --git a/VkTest/Config.hs b/VkTest/Config.hs
new file mode 100644
index 0000000..8f18aa2
--- /dev/null
+++ b/VkTest/Config.hs
@@ -0,0 +1,248 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE NondecreasingIndentation #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module VkTest.Config where
+
+import Data.Bits ((.|.))
+import Data.ByteString (ByteString)
+import Data.Int (Int32)
+import Data.Vector (Vector)
+import Data.Word (Word32)
+import FIR (compileTo, runCompilationsTH, ModuleRequirements)
+import Vulkan.CStruct.Extends (SomeStruct(..))
+import Vulkan.Zero (zero)
+
+import qualified Data.ByteString.Char8 as BSC
+import qualified Data.Vector as V
+import qualified VkTest.Shaders as Shaders
+import qualified Vulkan.Core10 as Vk
+import qualified Vulkan.Core13 as Vk
+import qualified Vulkan.Extensions.VK_KHR_surface as Vk
+import qualified Vulkan.Extensions.VK_KHR_swapchain as Vk
+
+height :: Int32
+height = 400
+width :: Int32
+width = 800
+
+layers :: Vector ByteString
+layers = V.fromList $ map BSC.pack ["VK_LAYER_KHRONOS_validation"]
+
+extensions :: Vector ByteString
+extensions = V.fromList $ map BSC.pack ["VK_KHR_swapchain"]
+
+vert :: Either a ModuleRequirements
+vert = $( runCompilationsTH [("Fragment Shader", compileTo Shaders.vertPath [] Shaders.vertex)] )
+
+frag :: Either a ModuleRequirements
+frag = $( runCompilationsTH [("Fragment Shader", compileTo Shaders.fragPath [] Shaders.fragment)] )
+
+data Consts = Consts
+ { title :: String
+ , vkApiVersion :: Word32
+ , framesInFlight :: Word32
+ }
+ deriving Show
+
+consts :: Consts
+consts =
+ Consts
+ { title = "game title"
+ , vkApiVersion = Vk.API_VERSION_1_3
+ , framesInFlight = 2
+ }
+
+-- for a given physical device and surface
+data QueriedData = QueriedData
+ { surfaceCapabilities :: Vk.SurfaceCapabilitiesKHR
+ , surfaceSupport :: [Int]
+ , surfaceFormats :: Vector Vk.SurfaceFormatKHR
+ , queueFamilyProperties :: Vector Vk.QueueFamilyProperties
+ , extensionProperties :: Vector Vk.ExtensionProperties
+ , physicalDeviceProperties :: Vk.PhysicalDeviceProperties
+ }
+ deriving Show
+
+instance' :: Vector ByteString -> Vk.InstanceCreateInfo '[]
+instance' exts =
+ zero { Vk.applicationInfo = Just (zero :: Vk.ApplicationInfo) { Vk.apiVersion = consts.vkApiVersion }
+ , Vk.enabledExtensionNames = exts
+ }
+
+device :: Word32 -> Vk.DeviceCreateInfo '[Vk.PhysicalDeviceVulkan13Features]
+device gqueueIndex =
+ zero { Vk.next = (vkFeatures13, ())
+ , Vk.queueCreateInfos = V.singleton $ SomeStruct zero { Vk.queueFamilyIndex = gqueueIndex
+ , Vk.queuePriorities = V.singleton 1
+ }
+ , Vk.enabledExtensionNames = extensions
+ }
+
+vkFeatures13 :: Vk.PhysicalDeviceVulkan13Features
+vkFeatures13 =
+ zero { Vk.synchronization2 = True
+ , Vk.dynamicRendering = True
+ }
+
+commandPool :: Word32 -> Vk.CommandPoolCreateInfo '[]
+commandPool gqueueIndex =
+ zero { Vk.queueFamilyIndex = gqueueIndex
+ , Vk.flags = Vk.COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
+ }
+
+commandBuffer :: Vk.CommandPool -> Vk.CommandBufferAllocateInfo
+commandBuffer gpool =
+ zero { Vk.commandPool = gpool
+ , Vk.level = Vk.COMMAND_BUFFER_LEVEL_PRIMARY
+ , Vk.commandBufferCount = consts.framesInFlight
+ }
+
+swapchain :: QueriedData -> Vk.SurfaceKHR -> Vk.SwapchainCreateInfoKHR '[]
+swapchain q surface =
+ zero { Vk.compositeAlpha = Vk.COMPOSITE_ALPHA_OPAQUE_BIT_KHR
+ , Vk.imageArrayLayers = 1
+ , Vk.imageColorSpace = Vk.COLORSPACE_SRGB_NONLINEAR_KHR
+ , Vk.imageExtent = q.surfaceCapabilities.currentExtent
+ , Vk.imageFormat = (V.head q.surfaceFormats).format
+ , Vk.imageUsage = Vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT
+ , Vk.minImageCount = q.surfaceCapabilities.minImageCount
+ , Vk.presentMode = Vk.PRESENT_MODE_FIFO_KHR
+ , Vk.preTransform = Vk.SURFACE_TRANSFORM_IDENTITY_BIT_KHR
+ , Vk.surface = surface
+ }
+
+imageViews :: Vector Vk.Image -> QueriedData -> Vector (Vk.ImageViewCreateInfo '[])
+imageViews images q =
+ V.map (\image -> (zero :: Vk.ImageViewCreateInfo '[]) { Vk.image = image
+ , Vk.viewType = Vk.IMAGE_VIEW_TYPE_2D
+ , Vk.subresourceRange = zero { Vk.aspectMask = Vk.IMAGE_ASPECT_COLOR_BIT
+ , Vk.levelCount = Vk.REMAINING_MIP_LEVELS
+ , Vk.layerCount = Vk.REMAINING_ARRAY_LAYERS
+ }
+ , Vk.format = (V.head q.surfaceFormats).format
+ }) images
+
+pipelineLayout :: Vk.PipelineLayoutCreateInfo
+pipelineLayout =
+ zero
+
+graphicsPipeline :: QueriedData -> Vk.ShaderModule -> Vk.ShaderModule -> Vk.PipelineLayout -> Vk.GraphicsPipelineCreateInfo '[Vk.PipelineRenderingCreateInfo]
+graphicsPipeline q vertMod fragMod pipelineLayout' =
+ zero { Vk.next = (pipelineRendering q, ())
+ , Vk.stages = V.map (SomeStruct) $ V.fromList [ zero { Vk.stage = Vk.SHADER_STAGE_VERTEX_BIT
+ , Vk.module' = vertMod
+ , Vk.name = "main"
+ }
+ , zero { Vk.stage = Vk.SHADER_STAGE_FRAGMENT_BIT
+ , Vk.module' = fragMod
+ , Vk.name = "main"
+ }]
+ , Vk.vertexInputState = Just zero
+ , Vk.inputAssemblyState = Just zero { Vk.topology = Vk.PRIMITIVE_TOPOLOGY_TRIANGLE_LIST }
+ , Vk.tessellationState = Nothing
+ , Vk.viewportState = Just $ SomeStruct zero { Vk.viewports = V.fromList [zero { Vk.x = 0
+ , Vk.y = 0
+ , Vk.width = fromIntegral q.surfaceCapabilities.currentExtent.width
+ , Vk.height = fromIntegral q.surfaceCapabilities.currentExtent.height
+ , Vk.minDepth = 0
+ , Vk.maxDepth = 1
+ }]
+ , Vk.scissors = V.fromList [(zero :: Vk.Rect2D) { Vk.offset = zero
+ , Vk.extent = q.surfaceCapabilities.currentExtent
+ }]
+ }
+
+ , Vk.rasterizationState = Just $ SomeStruct zero { Vk.lineWidth = 1 }
+ , Vk.multisampleState = Just $ SomeStruct zero { Vk.rasterizationSamples = Vk.SAMPLE_COUNT_1_BIT }
+ , Vk.depthStencilState = Just zero
+ , Vk.colorBlendState = Just $ SomeStruct zero { Vk.attachments = V.singleton zero { Vk.colorWriteMask = Vk.COLOR_COMPONENT_R_BIT .|. Vk.COLOR_COMPONENT_G_BIT .|. Vk.COLOR_COMPONENT_B_BIT .|. Vk.COLOR_COMPONENT_A_BIT }}
+ , Vk.dynamicState = Just zero { Vk.dynamicStates = V.fromList [Vk.DYNAMIC_STATE_VIEWPORT, Vk.DYNAMIC_STATE_SCISSOR] }
+ , Vk.layout = pipelineLayout'
+ }
+
+pipelineRendering :: QueriedData -> Vk.PipelineRenderingCreateInfo
+pipelineRendering q =
+ zero { Vk.colorAttachmentFormats = V.singleton (V.head q.surfaceFormats).format }
+
+preRenderPipelineBarrier :: Vk.Image -> Vk.DependencyInfo '[]
+preRenderPipelineBarrier image =
+ zero { Vk.imageMemoryBarriers = V.singleton $ SomeStruct zero { Vk.srcStageMask = Vk.PIPELINE_STAGE_2_NONE
+ , Vk.srcAccessMask = Vk.ACCESS_2_NONE
+ , Vk.dstStageMask = Vk.PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT
+ , Vk.dstAccessMask = Vk.ACCESS_2_COLOR_ATTACHMENT_READ_BIT .|. Vk.ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT
+ , Vk.oldLayout = Vk.IMAGE_LAYOUT_UNDEFINED
+ , Vk.newLayout = Vk.IMAGE_LAYOUT_ATTACHMENT_OPTIMAL
+ , Vk.image = image
+ , Vk.subresourceRange = zero { Vk.aspectMask = Vk.IMAGE_ASPECT_COLOR_BIT
+ , Vk.levelCount = 1
+ , Vk.layerCount = 1
+ }
+ }
+ }
+
+postRenderPipelineBarrier :: Vk.Image -> Vk.DependencyInfo '[]
+postRenderPipelineBarrier image =
+ zero { Vk.imageMemoryBarriers = V.singleton $ SomeStruct zero { Vk.srcStageMask = Vk.PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT
+ , Vk.srcAccessMask = Vk.ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT
+ , Vk.dstStageMask = Vk.PIPELINE_STAGE_2_NONE
+ , Vk.dstAccessMask = Vk.ACCESS_2_NONE
+ , Vk.oldLayout = Vk.IMAGE_LAYOUT_ATTACHMENT_OPTIMAL
+ , Vk.newLayout = Vk.IMAGE_LAYOUT_PRESENT_SRC_KHR
+ , Vk.image = image
+ , Vk.subresourceRange = zero { Vk.aspectMask = Vk.IMAGE_ASPECT_COLOR_BIT
+ , Vk.levelCount = 1
+ , Vk.layerCount = 1
+ }
+ }
+ }
+
+rendering :: QueriedData -> Vk.ImageView -> Vk.RenderingInfo '[]
+rendering q imageView =
+ zero { Vk.renderArea = zero { Vk.extent = q.surfaceCapabilities.currentExtent }
+ , Vk.layerCount = 1
+ , Vk.colorAttachments = V.singleton $ (SomeStruct) zero { Vk.imageView = imageView
+ , Vk.imageLayout = Vk.IMAGE_LAYOUT_ATTACHMENT_OPTIMAL
+ , Vk.loadOp = Vk.ATTACHMENT_LOAD_OP_CLEAR
+ , Vk.storeOp = Vk.ATTACHMENT_STORE_OP_STORE
+ , Vk.clearValue = Vk.Color $ Vk.Float32 0 0 1 1
+ }
+ }
+
+viewport :: QueriedData -> Vector Vk.Viewport
+viewport q =
+ V.singleton zero { Vk.x = 0
+ , Vk.y = 0
+ , Vk.width = fromIntegral q.surfaceCapabilities.currentExtent.width
+ , Vk.height = fromIntegral q.surfaceCapabilities.currentExtent.height
+ , Vk.minDepth = 0
+ , Vk.maxDepth = 1
+ }
+
+scissor :: QueriedData -> Vector Vk.Rect2D
+scissor q =
+ V.singleton (zero :: Vk.Rect2D) { Vk.offset = zero
+ , Vk.extent = q.surfaceCapabilities.currentExtent
+ }
+
+submitQueue :: Vk.CommandBuffer -> Vk.Semaphore -> Vk.Semaphore -> Vector (SomeStruct Vk.SubmitInfo2)
+submitQueue cbuffer sImageAcquired sRenderFinished =
+ V.singleton $ SomeStruct zero { Vk.waitSemaphoreInfos = V.singleton $ zero { Vk.semaphore = sImageAcquired
+ , Vk.stageMask = Vk.PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT
+ }
+ , Vk.commandBufferInfos = V.singleton $ SomeStruct zero { Vk.commandBuffer = Vk.commandBufferHandle cbuffer }
+ , Vk.signalSemaphoreInfos = V.singleton $ zero { Vk.semaphore = sRenderFinished
+ , Vk.stageMask = Vk.PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT
+ }
+ }
+
+present :: Vk.SwapchainKHR -> Word32 -> Vk.Semaphore -> Vk.PresentInfoKHR '[]
+present swapchain' imageIndex sRenderFinished =
+ zero { Vk.waitSemaphores = V.singleton sRenderFinished
+ , Vk.swapchains = V.singleton swapchain'
+ , Vk.imageIndices = V.singleton imageIndex
+ } \ No newline at end of file
diff --git a/VkTest/Shaders.hs b/VkTest/Shaders.hs
new file mode 100644
index 0000000..0d88325
--- /dev/null
+++ b/VkTest/Shaders.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE PartialTypeSignatures #-}
+{-# LANGUAGE RebindableSyntax #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeOperators #-}
+
+module VkTest.Shaders where
+
+import FIR
+import Math.Linear
+
+vertices = Vec3 (Vec2 0 (-0.5))
+ (Vec2 0.5 0.5)
+ (Vec2 (-0.5) 0.5)
+
+vertPath = "assets/shaders/vert.spv"
+fragPath = "assets/shaders/frag.spv"
+
+type VertexDefs =
+ '[ "main" ':-> EntryPoint '[] Vertex ]
+
+vertex :: ShaderModule "main" VertexShader VertexDefs _
+vertex = shader do
+ i <- get @"gl_VertexIndex"
+ (Vec2 x y) <- let' $ atv3v2f vertices i (Vec2 0 0)
+ put @"gl_Position" (Vec4 x y 0 1)
+
+type FragmentDefs =
+ '[ "main" ':-> EntryPoint '[ OriginUpperLeft ] Fragment
+ , "out_color" ':-> Output '[ Location 0 ] (V 4 Float)
+ ]
+
+fragment :: ShaderModule "main" FragmentShader FragmentDefs _
+fragment = shader do
+ put @"out_color" (Vec4 1.0 0.0 1.0 1.0)
+
+atv3v2f :: Code (V 3 (V 2 Float)) -> Code Word32 -> Code (V 2 Float) -> Code (V 2 Float)
+atv3v2f (Vec3 x y z) i d =
+ if i == 0 then x
+ else if i == 1 then y
+ else if i == 2 then z
+ else d \ No newline at end of file
diff --git a/VkTest/Util.hs b/VkTest/Util.hs
new file mode 100644
index 0000000..d92c2dd
--- /dev/null
+++ b/VkTest/Util.hs
@@ -0,0 +1,162 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE NondecreasingIndentation #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module VkTest.Util ( withRGFW
+ , withWindow
+ , withShaderModules
+ , withImageViews
+ , withSemaphores
+ , withFences
+ , pickPhysicalDevice
+ , processExtensions
+ , getGraphicsQueues
+ ) where
+
+import Data.Bits ((.&.))
+import Data.ByteString (ByteString)
+import Data.Coerce (coerce)
+import Data.Int (Int32)
+import Data.List ((\\))
+import Data.Vector (Vector)
+import Foreign.C
+import Foreign.C.ConstPtr (ConstPtr(..))
+import Foreign.Marshal.Array (advancePtr)
+import Foreign.Ptr (Ptr)
+import Foreign.Storable (peek)
+import Vulkan.Zero (zero)
+
+import VkTest.Config (QueriedData(..))
+
+import qualified VkTest.Config as Config
+
+import qualified Data.ByteString as BS
+import qualified Data.Vector as V
+import qualified RGFW as RGFW
+import qualified Vulkan.Core10 as Vk
+import qualified Vulkan.Extensions.VK_KHR_surface as Vk
+
+pickPhysicalDevice :: Vector Vk.PhysicalDevice -> Vk.SurfaceKHR -> IO (Vk.PhysicalDevice, QueriedData)
+pickPhysicalDevice pdevs surface = do
+ queriedDevs <- do
+ qs <- V.mapM (\pdev -> (queryPhysicalDevice pdev surface)) pdevs
+ return $ V.zip pdevs qs
+ let validpdevs = V.filter isValidPhysicalDevice queriedDevs
+ pickPhysicalDevice' validpdevs V.empty
+
+pickPhysicalDevice' :: Vector (Vk.PhysicalDevice, QueriedData) -> Vector (Vk.PhysicalDevice, QueriedData) -> IO (Vk.PhysicalDevice, QueriedData)
+pickPhysicalDevice' pdevs opts =
+ if V.null pdevs
+ then return $ (V.head opts)
+ else do
+ let pdev = V.head pdevs
+ case (snd pdev).physicalDeviceProperties.deviceType of
+ Vk.PHYSICAL_DEVICE_TYPE_DISCRETE_GPU -> pickPhysicalDevice' (V.tail pdevs) (V.cons pdev opts)
+ Vk.PHYSICAL_DEVICE_TYPE_CPU -> pickPhysicalDevice' (V.tail pdevs) opts
+ _ -> pickPhysicalDevice' (V.tail pdevs) (V.snoc opts pdev)
+
+-- returns list of queueFamilyIndex whick support a graphics pipeline
+getGraphicsQueues :: QueriedData -> [Int]
+getGraphicsQueues q = getGraphicsQueues' q.queueFamilyProperties 0 []
+getGraphicsQueues' :: Vector Vk.QueueFamilyProperties -> Int -> [Int] -> [Int]
+getGraphicsQueues' qfprops i is =
+ if V.null qfprops then is else
+ if zero /= (Vk.QUEUE_GRAPHICS_BIT .&. (V.head qfprops).queueFlags)
+ then getGraphicsQueues' (V.tail qfprops) (i + 1) (i:is)
+ else getGraphicsQueues' (V.tail qfprops) (i + 1) is
+
+-- returns list of queueFamilyIndex which support presentation
+getSurfaceSupport :: Vk.PhysicalDevice -> Vk.SurfaceKHR -> IO [Int]
+getSurfaceSupport pdev surface = do
+ qfprops <- Vk.getPhysicalDeviceQueueFamilyProperties pdev
+ getSurfaceSupport' pdev surface (V.length qfprops - 1) []
+
+getSurfaceSupport' :: Vk.PhysicalDevice -> Vk.SurfaceKHR -> Int -> [Int] -> IO [Int]
+getSurfaceSupport' pdev surface i is = if i < 0 then return is else do
+ support <- Vk.getPhysicalDeviceSurfaceSupportKHR pdev (fromIntegral i) surface
+ if support
+ then getSurfaceSupport' pdev surface (i - 1) (i:is)
+ else getSurfaceSupport' pdev surface (i - 1) is
+
+queryPhysicalDevice :: Vk.PhysicalDevice -> Vk.SurfaceKHR -> IO QueriedData
+queryPhysicalDevice pdev surface = do
+ qfprops <- Vk.getPhysicalDeviceQueueFamilyProperties pdev
+ sSupport <- getSurfaceSupport pdev surface
+ (_, extps) <- Vk.enumerateDeviceExtensionProperties pdev Nothing
+ caps <- Vk.getPhysicalDeviceSurfaceCapabilitiesKHR pdev surface
+ (_, forms) <- Vk.getPhysicalDeviceSurfaceFormatsKHR pdev surface
+ props <- Vk.getPhysicalDeviceProperties pdev
+ return QueriedData { surfaceCapabilities = caps
+ , surfaceSupport = sSupport
+ , surfaceFormats = forms
+ , queueFamilyProperties = qfprops
+ , extensionProperties = extps
+ , physicalDeviceProperties = props
+ }
+
+-- checks that a device has the requisite capabilities
+isValidPhysicalDevice :: (Vk.PhysicalDevice, QueriedData) -> Bool
+isValidPhysicalDevice (_, q) =
+ if null $ getGraphicsQueues q
+ then False
+ else if null q.surfaceSupport
+ then False
+ else if [] /= ((V.toList Config.extensions) \\ (V.toList $ V.map (\p -> p.extensionName) q.extensionProperties))
+ then False
+ else True
+
+-- type conversion
+processExtensions :: CSize -> Ptr (ConstPtr CChar) -> Vector ByteString -> IO (Vector ByteString)
+processExtensions 0 _ extNames = return extNames
+processExtensions count strs extNames = do
+ str <- peek strs
+ extName <- BS.packCString (coerce str)
+ processExtensions (count - 1) (advancePtr strs 1) $ V.snoc extNames extName
+
+withFences :: Vk.Device -> Vector (Vk.FenceCreateInfo '[]) -> Maybe Vk.AllocationCallbacks -> (Vector Vk.Fence -> IO r) -> IO r
+withFences dev infos alloc io = do
+ fs <- mapM (\info -> Vk.createFence dev info alloc) infos
+ o0 <- io fs
+ _ <- mapM (\f -> Vk.destroyFence dev f alloc) fs
+ return o0
+
+withSemaphores :: Vk.Device -> Vector (Vk.SemaphoreCreateInfo '[]) -> Maybe Vk.AllocationCallbacks -> (Vector Vk.Semaphore -> IO r) -> IO r
+withSemaphores dev infos alloc io = do
+ ss <- mapM (\info -> Vk.createSemaphore dev info alloc) infos
+ o0 <- io ss
+ _ <- mapM (\s -> Vk.destroySemaphore dev s alloc) ss
+ return o0
+
+withImageViews :: Vk.Device -> Vector (Vk.ImageViewCreateInfo '[]) -> Maybe Vk.AllocationCallbacks -> (Vector Vk.ImageView -> IO r) -> IO r
+withImageViews dev infos alloc io = do
+ imageViews <- mapM (\info -> Vk.createImageView dev info alloc) infos
+ o0 <- io imageViews
+ _ <- mapM (\imageView -> Vk.destroyImageView dev imageView alloc) imageViews
+ return o0
+
+withShaderModules :: Vk.Device -> Vector (Vk.ShaderModuleCreateInfo '[]) -> Maybe Vk.AllocationCallbacks -> (Vector Vk.ShaderModule -> IO r) -> IO r
+withShaderModules dev infos alloc io = do
+ mods <- mapM (\info -> Vk.createShaderModule dev info alloc) infos
+ o0 <- io mods
+ _ <- mapM (\mod' -> Vk.destroyShaderModule dev mod' alloc) mods
+ return o0
+
+withWindow :: String -> Int32 -> Int32 -> Int32 -> Int32 -> RGFW.RGFW_windowFlags -> (Ptr RGFW.RGFW_window -> IO r) -> IO r
+withWindow name x y w h flags io =
+ withCString name $ \str -> do
+ window <- RGFW.rGFW_createWindow (ConstPtr str) (RGFW.I32 x) (RGFW.I32 y) (RGFW.I32 w) (RGFW.I32 h) flags
+ o0 <- io window
+ RGFW.rGFW_window_close window
+ return o0
+
+withRGFW :: String -> RGFW.RGFW_initFlags -> (Int32 -> IO r) -> IO r
+withRGFW title flags io =
+ withCString title $ \str -> do
+ ret_code <- RGFW.rGFW_init (ConstPtr str) flags
+ o0 <- io $ fromIntegral ret_code
+ RGFW.rGFW_deinit
+ return o0 \ No newline at end of file