Skip to content

Commit 50e8ca9

Browse files
authored
Add init helpers for sdl2 and glfw (#627)
1 parent 58500d2 commit 50e8ca9

45 files changed

Lines changed: 1862 additions & 1251 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
triangle.png
2+
julia.png
3+
4+
stack.yaml.lock
15
dist
26
dist-*
37
cabal-dev

cabal.project

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ packages:
33
./openxr
44
./VulkanMemoryAllocator
55
./utils
6+
./utils-init/vulkan-init-sdl2
7+
./utils-init/vulkan-init-glfw
68
./examples
79
./generate-new/
810

default.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let
2121
(pkgs.haskell.lib.dontCheck vulkan)
2222
(pkgs.haskell.lib.dontCheck VulkanMemoryAllocator)
2323
] else
24-
[ vulkan vulkan-utils VulkanMemoryAllocator vulkan-examples openxr ]
24+
[ vulkan vulkan-utils vulkan-init-sdl2 vulkan-init-glfw VulkanMemoryAllocator vulkan-examples openxr ]
2525
++ pkgs.lib.optional (p.ghc.version == generator-ghc-version) generate-new;
2626

2727
in if forShell then

examples/compute/Main.hs

Lines changed: 56 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,11 @@ import AutoApply
1414
import qualified Codec.Picture as JP
1515
import Control.Exception.Safe
1616
import Control.Monad.IO.Class
17-
import Control.Monad.Trans.Maybe ( MaybeT(..) )
1817
import Control.Monad.Trans.Reader
1918
import Control.Monad.Trans.Resource
2019
import Data.Bits
2120
import qualified Data.ByteString.Lazy as BSL
22-
import Data.Foldable
23-
import Data.List ( partition )
24-
import Data.Maybe ( catMaybes )
25-
import Data.Ord ( comparing )
26-
import Data.Text ( Text )
27-
import qualified Data.Text as T
28-
import Data.Text.Encoding ( decodeUtf8 )
21+
import Data.Functor.Identity ( Identity(..) )
2922
import qualified Data.Vector as V
3023
import Data.Word
3124
import Foreign.Marshal.Array ( peekArray )
@@ -55,8 +48,18 @@ import Vulkan.Dynamic ( DeviceCmds
5548
)
5649
)
5750
import Vulkan.Extensions.VK_EXT_debug_utils
58-
import Vulkan.Extensions.VK_EXT_validation_features
59-
import Vulkan.Utils.Debug
51+
import Vulkan.Requirement ( InstanceRequirement(..) )
52+
import Vulkan.Utils.Debug ( debugCallbackPtr )
53+
import qualified Vulkan.Utils.Init.Headless as Init
54+
import Vulkan.Utils.Initialization ( createDeviceFromRequirements
55+
, physicalDeviceName
56+
, pickPhysicalDevice
57+
)
58+
import Vulkan.Utils.QueueAssignment ( QueueFamilyIndex(..)
59+
, QueueSpec(..)
60+
, assignQueues
61+
, isComputeQueueFamily
62+
)
6063
import Vulkan.Utils.ShaderQQ.GLSL.Glslang
6164
import Vulkan.Zero
6265
import VulkanMemoryAllocator as VMA
@@ -146,7 +149,6 @@ autoapplyDecs
146149
, 'withCommandPool
147150
, 'withFence
148151
, 'withComputePipelines
149-
, 'withInstance
150152
, 'withPipelineLayout
151153
, 'withShaderModule
152154
, 'withDescriptorPool
@@ -405,31 +407,22 @@ createShader = do
405407
myApiVersion :: Word32
406408
myApiVersion = API_VERSION_1_0
407409

408-
-- | Create an instance with a debug messenger
410+
-- | Create an instance with a debug messenger and validation layer.
409411
createInstance :: MonadResource m => m Instance
410412
createInstance = do
411-
availableExtensionNames <-
412-
toList
413-
. fmap extensionName
414-
. snd
415-
<$> enumerateInstanceExtensionProperties Nothing
416-
availableLayerNames <-
417-
toList . fmap layerName . snd <$> enumerateInstanceLayerProperties
418-
419-
let requiredLayers = []
420-
optionalLayers = ["VK_LAYER_KHRONOS_validation"]
421-
requiredExtensions = [EXT_DEBUG_UTILS_EXTENSION_NAME]
422-
optionalExtensions = [EXT_VALIDATION_FEATURES_EXTENSION_NAME]
423-
424-
extensions <- partitionOptReq "extension"
425-
availableExtensionNames
426-
optionalExtensions
427-
requiredExtensions
428-
layers <- partitionOptReq "layer"
429-
availableLayerNames
430-
optionalLayers
431-
requiredLayers
432-
413+
inst <- Init.withInstance
414+
(Just zero { applicationName = Nothing, apiVersion = myApiVersion })
415+
[ RequireInstanceExtension
416+
{ instanceExtensionLayerName = Nothing
417+
, instanceExtensionName = EXT_DEBUG_UTILS_EXTENSION_NAME
418+
, instanceExtensionMinVersion = minBound
419+
}
420+
]
421+
[ RequireInstanceLayer
422+
{ instanceLayerName = "VK_LAYER_KHRONOS_validation"
423+
, instanceLayerMinVersion = minBound
424+
}
425+
]
433426
let debugMessengerCreateInfo = zero
434427
{ messageSeverity = DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
435428
.|. DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
@@ -438,20 +431,6 @@ createInstance = do
438431
.|. DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT
439432
, pfnUserCallback = debugCallbackPtr
440433
}
441-
instanceCreateInfo =
442-
zero
443-
{ applicationInfo = Just zero { applicationName = Nothing
444-
, apiVersion = myApiVersion
445-
}
446-
, enabledLayerNames = V.fromList layers
447-
, enabledExtensionNames = V.fromList extensions
448-
}
449-
::& debugMessengerCreateInfo
450-
:& ValidationFeaturesEXT
451-
[VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT]
452-
[]
453-
:& ()
454-
(_, inst) <- withInstance' instanceCreateInfo
455434
_ <- withDebugUtilsMessengerEXT inst debugMessengerCreateInfo Nothing allocate
456435
pure inst
457436

@@ -460,89 +439,38 @@ createDevice
460439
=> Instance
461440
-> m (PhysicalDevice, PhysicalDeviceInfo, Device)
462441
createDevice inst = do
463-
(pdi, phys) <- pickPhysicalDevice inst physicalDeviceInfo
442+
mPd <- pickPhysicalDevice inst hasComputeQueue id
443+
(_, phys) <- maybe (throwString "Unable to find appropriate PhysicalDevice")
444+
pure
445+
mPd
464446
sayErr . ("Using device: " <>) =<< physicalDeviceName phys
465447

466-
let deviceCreateInfo = zero
467-
{ queueCreateInfos =
468-
[ SomeStruct zero { queueFamilyIndex = pdiComputeQueueFamilyIndex pdi
469-
, queuePriorities = [1]
470-
}
471-
]
472-
}
473-
474-
(_, dev) <- withDevice phys deviceCreateInfo Nothing allocate
475-
pure (phys, pdi, dev)
476-
477-
----------------------------------------------------------------
478-
-- Physical device tools
479-
----------------------------------------------------------------
480-
481-
-- | Get a single PhysicalDevice deciding with a scoring function
482-
pickPhysicalDevice
483-
:: (MonadIO m, MonadThrow m, Ord a)
484-
=> Instance
485-
-> (PhysicalDevice -> m (Maybe a))
486-
-- ^ Some "score" for a PhysicalDevice, Nothing if it is not to be chosen.
487-
-> m (a, PhysicalDevice)
488-
pickPhysicalDevice inst devScore = do
489-
(_, devs) <- enumeratePhysicalDevices inst
490-
scores <- catMaybes
491-
<$> sequence [ fmap (, d) <$> devScore d | d <- toList devs ]
492-
case scores of
493-
[] -> throwString "Unable to find appropriate PhysicalDevice"
494-
_ -> pure (maximumBy (comparing fst) scores)
495-
496-
-- | The Ord instance prioritises devices with more memory
497-
data PhysicalDeviceInfo = PhysicalDeviceInfo
498-
{ pdiTotalMemory :: Word64
499-
, pdiComputeQueueFamilyIndex :: Word32
448+
mAssign <- assignQueues
449+
phys
450+
(Identity (QueueSpec 1 (\_ q -> pure (isComputeQueueFamily q))))
451+
(qInfos, getQs) <- maybe (throwString "Unable to assign compute queue")
452+
pure
453+
mAssign
454+
dev <- createDeviceFromRequirements
455+
[]
456+
[]
457+
phys
458+
zero { queueCreateInfos = SomeStruct <$> qInfos }
459+
Identity (QueueFamilyIndex computeFamilyIdx, _q) <- liftIO (getQs dev)
460+
pure (phys, PhysicalDeviceInfo computeFamilyIdx, dev)
461+
where
462+
hasComputeQueue :: MonadIO m => PhysicalDevice -> m (Maybe Word64)
463+
hasComputeQueue phys = do
464+
qProps <- getPhysicalDeviceQueueFamilyProperties phys
465+
if V.any isComputeQueueFamily qProps
466+
then do
467+
heaps <- memoryHeaps <$> getPhysicalDeviceMemoryProperties phys
468+
pure (Just (sum (DI.size <$> heaps)))
469+
else pure Nothing
470+
471+
newtype PhysicalDeviceInfo = PhysicalDeviceInfo
472+
{ pdiComputeQueueFamilyIndex :: Word32
500473
-- ^ The queue family index of the first compute queue
501474
}
502475
deriving (Eq, Ord)
503476

504-
physicalDeviceInfo
505-
:: MonadIO m => PhysicalDevice -> m (Maybe PhysicalDeviceInfo)
506-
physicalDeviceInfo phys = runMaybeT $ do
507-
pdiTotalMemory <- do
508-
heaps <- memoryHeaps <$> getPhysicalDeviceMemoryProperties phys
509-
pure $ sum (DI.size <$> heaps)
510-
pdiComputeQueueFamilyIndex <- do
511-
queueFamilyProperties <- getPhysicalDeviceQueueFamilyProperties phys
512-
let isComputeQueue q =
513-
(QUEUE_COMPUTE_BIT .&&. queueFlags q) && (queueCount q > 0)
514-
computeQueueIndices = fromIntegral . fst <$> V.filter
515-
(isComputeQueue . snd)
516-
(V.indexed queueFamilyProperties)
517-
MaybeT (pure $ computeQueueIndices V.!? 0)
518-
pure PhysicalDeviceInfo { .. }
519-
520-
physicalDeviceName :: MonadIO m => PhysicalDevice -> m Text
521-
physicalDeviceName phys = do
522-
props <- getPhysicalDeviceProperties phys
523-
pure $ decodeUtf8 (deviceName props)
524-
525-
----------------------------------------------------------------
526-
-- Utils
527-
----------------------------------------------------------------
528-
529-
partitionOptReq
530-
:: (Show a, Eq a, MonadIO m) => Text -> [a] -> [a] -> [a] -> m [a]
531-
partitionOptReq type' available optional required = do
532-
let (optHave, optMissing) = partition (`elem` available) optional
533-
(reqHave, reqMissing) = partition (`elem` available) required
534-
tShow = T.pack . show
535-
for_ optMissing
536-
$ \n -> sayErr $ "Missing optional " <> type' <> ": " <> tShow n
537-
case reqMissing of
538-
[] -> pure ()
539-
[x] -> sayErr $ "Missing required " <> type' <> ": " <> tShow x
540-
xs -> sayErr $ "Missing required " <> type' <> "s: " <> tShow xs
541-
pure (reqHave <> optHave)
542-
543-
----------------------------------------------------------------
544-
-- Bit utils
545-
----------------------------------------------------------------
546-
547-
(.&&.) :: Bits a => a -> a -> Bool
548-
x .&&. y = (/= zeroBits) (x .&. y)

examples/hie.yaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ cradle:
66
- path: "./info/"
77
component: "exe:info"
88

9-
- path: "./sdl-triangle/"
10-
component: "exe:sdl-triangle"
9+
- path: "./triangle-sdl2/"
10+
component: "exe:triangle-sdl2"
1111

12-
- path: "./offscreen/"
13-
component: "exe:offscreen"
12+
- path: "./triangle-glfw/"
13+
component: "exe:triangle-glfw"
14+
15+
- path: "./triangle-headless/"
16+
component: "exe:triangle-headless"
1417

1518
- path: "./compute/"
1619
component: "exe:compute"

examples/hlsl/Init.hs

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import Vulkan.Core12.Promoted_From_VK_KHR_timeline_semaphore
1919
import Vulkan.Extensions.VK_KHR_timeline_semaphore
2020

2121
import Control.Applicative
22-
import qualified Data.ByteString as BS
2322
import Data.Foldable ( for_ )
2423
import Data.Vector ( Vector )
2524
import GHC.IO.Exception ( IOErrorType(NoSuchThing)
@@ -29,7 +28,6 @@ import MonadVulkan ( Queues(..)
2928
, checkCommands
3029
)
3130
import qualified SDL.Video as SDL
32-
import qualified SDL.Video.Vulkan as SDL
3331
import Vulkan.CStruct.Extends
3432
import Vulkan.Core10 as Vk
3533
hiding ( withBuffer
@@ -44,6 +42,7 @@ import Vulkan.Extensions.VK_KHR_get_physical_device_properties2
4442
import Vulkan.Extensions.VK_KHR_surface
4543
import Vulkan.Extensions.VK_KHR_swapchain
4644
import Vulkan.Requirement
45+
import qualified Vulkan.Utils.Init.SDL2 as VkInit
4746
import Vulkan.Utils.Initialization
4847
import Vulkan.Utils.QueueAssignment
4948
import qualified Vulkan.Utils.Requirements.TH as U
@@ -53,7 +52,7 @@ import VulkanMemoryAllocator ( Allocator
5352
, VulkanFunctions(..)
5453
, withAllocator
5554
)
56-
import Window
55+
import Window.SDL2
5756
import Foreign.Ptr (castFunPtr)
5857

5958
myApiVersion :: Word32
@@ -63,22 +62,16 @@ myApiVersion = API_VERSION_1_0
6362
-- Instance Creation
6463
----------------------------------------------------------------
6564

66-
-- | Create an instance with a debug messenger
6765
createInstance :: MonadResource m => SDL.Window -> m Instance
68-
createInstance win = do
69-
windowExtensions <-
70-
liftIO $ traverse BS.packCString =<< SDL.vkGetInstanceExtensions win
71-
let createInfo = zero
72-
{ applicationInfo = Just zero { applicationName = Nothing
73-
, apiVersion = myApiVersion
74-
}
75-
}
76-
reqs =
77-
(\n -> RequireInstanceExtension Nothing n minBound)
78-
<$> ( KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME
79-
: windowExtensions
80-
)
81-
createDebugInstanceFromRequirements reqs [] createInfo
66+
createInstance win = VkInit.withInstance
67+
win
68+
(Just zero { applicationName = Nothing, apiVersion = myApiVersion })
69+
[ RequireInstanceExtension
70+
Nothing
71+
KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME
72+
minBound
73+
]
74+
[]
8275

8376
----------------------------------------------------------------
8477
-- Device creation

examples/hlsl/Main.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import SDL ( showWindow
1212
)
1313
import Swapchain ( threwSwapchainError )
1414
import Utils
15-
import Window
15+
import Window.SDL2
1616

1717
main :: IO ()
1818
main = runResourceT $ do

0 commit comments

Comments
 (0)