Giter VIP home page Giter VIP logo

vulkan's Introduction

Slightly high level Haskell bindings to the Vulkan graphics API.

Join us on Matrix at #vulkan:monoid.al

These bindings present an interface to Vulkan which looks like more idiomatic Haskell and which is much less verbose than the C API. Nevertheless, it retains access to all the functionality. If you find something you can do in the C bindings but not in these high level bindings please raise an issue.

Practically speaking this means:

  • No fiddling with vkGetInstanceProcAddr or vkGetDeviceProcAddr to get function pointers, this is done automatically on instance and device creation1.

  • No setting the sType member, this is done automatically.

  • No passing length/pointer pairs for arrays, Vector is used instead2.

  • No passing pointers for return values, this is done for you and multiple results are returned as elements of a tuple.

  • No checking VkResult return values for failure, a VulkanException will be thrown if a Vulkan command returns an error VkResult.

  • No manual memory management for command parameters or Vulkan structs. You'll still have to manage buffer and image memory yourself however.

Package structure

Types and functions are placed into modules according to the features and extensions portions of the specification. As these sections only mention functions, a best guess has to be made for types. Types and constants are drawn in transitively according to the dependencies of the functions.

It should be sufficient to import Vulkan.CoreXX along with Vulkan.Extensions.{whatever extensions you want}. You might want to import Vulkan.Zero too.

These bindings are intended to be imported qualified and do not feature the Vk prefixes on commands, structures, members or constants.

Things to know

  • Documentation is included more or less verbatim from the Vulkan C API documentation. The parameters it references might not map one-to-one with what's in these bindings. It should be obvious in most cases what it's trying to say. If part of the documentation is misleading or unclear with respect to these Haskell bindings please open an issue and we can special case a fix.

    • The haddock documentation can be browsed on Hackage or here
  • Parameters are named with the ::: operator where it would be useful; this operator simply ignores the string on the left.

  • There exists a Zero type class defined in Vulkan.Zero. This is a class for initializing values with all zero contents and empty arrays. It's very handy when initializing structs to use something like zero { only = _, members = _, i = _, care = _, about = _ }.

  • The library is compiled with -XStrict so expect all record members to be strict (and unboxed when they're small)

  • Calls to Vulkan are marked as unsafe by default to reduce FFI overhead.

    • This can be changed by setting the safe-foreign-calls flag.
    • It means that Vulkan functions are unable to safely call Haskell code. See the Haskell wiki for more information. This is important to consider if you want to write allocation or debug callbacks in Haskell.
    • It's also means that the garbage collector will not run while these calls are in progress. For some blocking functions (those which can return VK_TIMEOUT and those with wait in the name) a safe version is also provided with the Safe suffix.
  • As encouraged by the Vulkan user guide, commands are linked dynamically (with the sole exception of vkGetInstanceProcAddr).

    • The function pointers are attached to any dispatchable handle to save you the trouble of passing them around.
    • The function pointers are retrieved by calling vkGetInstanceProcAddr and vkGetDeviceProcAddr. These are stored in two records InstanceCmds and DeviceCmds which store instance level and device level commands respectively. These tables can be initialized with the initInstanceCmds and initDeviceCmds found in Vulkan.Dynamic.
  • There are nice Read and Show instances for the enums and bitmasks. These will, where possible, print and parse the pattern synonyms. For example one can do the following:

    > read @COMPARE_OP "COMPARE_OP_LESS"
    COMPARE_OP_LESS
  • Make sure that all the functions you're going to use are not nullPtr in InstanceCmds or DeviceCmds before calling them or the command will throw an IOException. The *Cmds records can be found inside any dispatchable handle.

Minor things

  • To prevent a name clash between the constructors of VkClearColorValue and VkPerformanceCounterResultKHR the latter have had Counter suffixed.

  • To prevent a name clash between the constructors of DeviceOrHostAddressKHR and DeviceOrHostAddressConstKHR the latter have had Const suffixed.

How the C types relate to Haskell types

These bindings take advantage of the meta information present in the specification detailing the validity of structures and arguments.

  • Vector is used in place of pointers to arrays with associated length members/parameters. When interfacing with Vulkan these bindings automatically set the length member/parameter properly. If the vector is optional but the length is not then the length member/parameter is preserved, but will be inferred if the vector is present and the length is 0.

  • If a struct member or command parameters in the specification is a optional pointer (it may be null) this is replaced with a Maybe value.

  • If a struct has a member which can only have one possible value (the most common example is the sType member, then this member is elided.

  • C strings become ByteString. This is also the case for fixed length C strings, the library will truncate overly long strings in this case.

  • Pointers to void accompanied by a length in bytes become ByteString

  • Shader code is represented as ByteString

  • VkBool32 becomes Bool

  • Some Vulkan commands or structs take several arrays which must be the same length. These are currently exposed as several Vector arguments which must be the same length. If they are not the same length an exception is thrown.

  • Vulkan structs with bitfields have them split into their component parts in the Haskell record. Then marshalling to and from C the masking and shifting takes place automatically.

If anything is unclear please raise an issue. The marshaling to and from Haskell and C is automatically generated and I've not checked every single function. It's possible that there are some commands or structs which could be represented better in Haskell, if so please also raise an issue.

Vulkan errors

If a Vulkan command has the VkResult type as a return value, this is checked and a VulkanException is thrown if it is not a success code. If the only success code a command can return is VK_SUCCESS then this is elided from the return type. If a command can return other success codes, for instance VK_EVENT_SET then the success code is exposed.

Bracketing commands

There are certain sets commands which must be called in pairs, for instance the create and destroy commands for using resources. In order to facilitate safe use of these commands, (i.e. ensure that the corresponding destroy command is always called) these bindings expose similarly named commands prefixed with with (for Create/Destroy and Allocate/Free pairs) or use for (Begin/End pairs). If the command is used in command buffer building then it is additionally prefixed with cmd.

These are higher order functions which take as their last argument a consumer for a pair of create and destroy commands. Values which fit this hole include Control.Exception.bracket, Control.Monad.Trans.Resource.allocate and (,).

An example is withInstance which calls createInstance and destroyInstance. Notice how the AllocationCallbacks parameter is automatically passed to the createInstance and destroyInstance command.

createInstance
  :: forall a m
   . (PokeChain a, MonadIO m)
  => InstanceCreateInfo a
  -> Maybe AllocationCallbacks
  -> m Instance

destroyInstance
  :: forall m
   . MonadIO m
  => Instance
  -> Maybe AllocationCallbacks
  -> m ()

withInstance
  :: forall a m r
   . (PokeChain a, MonadIO m)
  => InstanceCreateInfo a
  -> Maybe AllocationCallbacks
  -> (m Instance -> (Instance -> m ()) -> r)
  -> r

Example usage:

import Control.Monad.Trans.Resource (runResourceT, allocate)
-- Create an instance and print its value
main = runResourceT $ do
  (instanceReleaseKey, inst) <- withInstance zero Nothing allocate
  liftIO $ print inst

-- Begin a render pass, draw something and end the render pass
drawTriangle =
  cmdUseRenderPass buffer renderPassBeginInfo SUBPASS_CONTENTS_INLINE bracket_
    $ do
        cmdBindPipeline buffer PIPELINE_BIND_POINT_GRAPHICS graphicsPipeline
        cmdDraw buffer 3 1 0 0

These pairs of commands aren't explicit in the specification, so a list of them is maintained in the generation code, if you see something missing please open an issue (these pairs are generated in VK/Bracket.hs).

Dual use commands

Certain commands, such as vkEnumerateDeviceLayerProperties or vkGetDisplayModePropertiesKHR, have a dual use. If they are not given a pointer to return an array of results then they instead return the total number of possible results, otherwise they return a number of results. There is an idiom in Vulkan which involves calling this function once with a null pointer to get the total number of queryable values, allocating space for querying that many values and they calling the function again to get the values. These bindings expose commands which automatically return all the results. As an example enumeratePhysicalDevices has the type MonadIO m => Instance -> m (Result, Vector PhysicalDevice).

Structure chains

Most structures in Vulkan have a member called pNext which can be a pointer to another Vulkan structure containing additional information. In these high level bindings the head of any struct chain is parameterized over the rest of the items in the chain. This allows for using type inference for getting struct chain return values out of Vulkan, for example: getPhysicalDeviceFeatures2 :: (PokeChain a, PeekChain a) => PhysicalDevice -> IO (PysicalDeviceFeatures2 a); here the variable a :: [Type] represents the structures present in the chain returned from vkGetPhysicalDeviceFeatures2.

There exists a GADT SomeStruct which captures the case of an unknown tail in the struct chain. This is also used for nested chains inside structs.

Struct chains inside records are represented as nested tuples: next :: (Something, (SomethingElse, (AThirdThing, ())))

There are two pattern synonyms exposed in Vulkan.CStruct.Extends which help in constructing and deconstructing struct chains.

  • h ::& t which appends the tail t to the struct h
  • t :& ts which constructs a struct extending tail comprising struct t and structs ts. Note that you must terminate the list with ().

For example, to create an instance with a debugUtilsMessenger and the validation layer's best practices output enabled:

makeInst = do
  let debugCreateInfo = _ :: DebugUtilsMessengerCreateInfoEXT
      validationFeatures = ValidationFeaturesEXT [VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT] []
      instanceCreateInfo = zero ::& debugCreateInfo :& validationFeatures :& ()
  createInstance instanceCreateInfo Nothing

And to deconstruct a return value with a struct tail, for example to find out if a physical device supports Timeline Semaphores:

hasTimelineSemaphores phys = do
  _ ::& PhysicalDeviceTimelineSemaphoreFeatures hasTimelineSemaphores :& () <-
    getPhysicalDeviceFeatures2 phys
  pure hasTimelineSemaphores

-- If you don't have a MonadFail instance you'll have to avoid pattern matching
-- using do notation because of https://gitlab.haskell.org/ghc/ghc/-/issues/15681
hasTimelineSemaphores phys = do
  feats <- getPhysicalDeviceFeatures2 phys
  let _ ::& PhysicalDeviceTimelineSemaphoreFeatures hasTimelineSemaphores :& ()
       = feats
  pure hasTimelineSemaphores

Building

This package requires GHC 8.6 or higher due to the use of the QuantifiedConstraints language extension.

Make sure you have initialized the VulkanMemoryAllocator submodule if you intend to build the VulkanMemoryAllocator package.

If you provision libvulkan.so (the Vulkan loader) with nix and you're not on NixOS, you'll have to use NixGL to run your programs. For this reason it's recommended to use the system-provided libvulkan.so.

For instructions on how to regenerate the bindings see the readme in ./generate-new.

To build the example programs. You'll need to supply the following system packages:

  • vulkan-loader (for libvulkan.so)
  • vulkan-headers (for vulkan.h)
  • pkg-config and SDL2 to build the Haskell sdl2 package.
  • glslang (for the glslangValidator binary, to build the shaders)

Jonathan Merritt has made an excellent video detailing how to set up everything necessary for running the examples on macOS here.

Building using Nix

Here is some generally useful information for using the default.nix files in this repo.

default.nix { forShell = false; } evaluates to an attribute set with one attribute for each of the following packages:

  • vulkan, the main package of this repository
  • VulkanMemoryAllocator, bindings to VMA
  • vulkan-utils, a small selection of utility functions for using vulkan
  • vulkan-examples, some examples, this package is dependency-heavy
  • generate-new, the program to generate the source of vulkan and VulkanMemoryAllocator, also quite dependency-heavy (this only build with ghc 8.10).

You may want to pass your <nixpkgs> as pkgs to default.nix to avoid rebuilding a parallel set of haskell packages based on the pegged nixpkgs version in default.nix. It should probably work with a wide range of nixpkgss, however some overrides in default.nix may need tweaking,

Alternatively you could use the Cachix repo https://app.cachix.org/cache/vulkan-haskell which contains the latest closure for the packages in this repo.

nix-build -A vulkan is probably not terribly useful for using the library as it just builds the Haskell library.

nix-build -A vulkan-examples will produce a path with several examples, however to run these on a non-NixOS platform you'll need to use the NixGL project (or something similar) to run these. This isn't something tested very often so may be a little fragile. I'd suggest for non-NixOS platforms compiling without using Nix (or better yet get reliable instructions for using NixGL and open a PR).

This library is currently up to date on nixpkgs master (as of https://github.com/NixOS/nixpkgs/commit/af9608d6d133ad9b6de712418db52603bbc8531c 2020-06-23), so if you're just a consumer it might be best to just use haskellPackages.vulkan from a recent version there.

For using this repository, I have two workflows:

  • For building and running examples

    • I navigate to the examples directory and use the default.nix expression in there to provision a shell with the correct dependencies for the examples.
    • I also make a cabal.project containing packages: ./, the reason for this little dance instead of just using the root's default.nix is so that nix builds the hoogle database for the dependencies and HIE's completion and indexing works much better for external dependencies instead of using a multi-package project as is the root.
    • This will override nixpkgs's vulkan and VulkanMemoryAllocator libraries with the ones in the repo, as well as building vulkan-utils.
  • For modifying the generation program I navigate to the generate-new directory and run nix-shell .. to use default.nix in the repo's root to provision a shell with:

    • The dependencies for running the generator
    • And the dependencies for compiling the vulkan source it spits out.
    • I run the generator with ghci $(HIE_BIOS_OUTPUT=/dev/stdout ./flags.sh $(pwd)/vk/Main.hs) vk/Main.hs +RTS -N16

For using the source in this package externally it may be easiest to do whatever you do to get a haskell environment with nix and simply override the source to point to this repo, the dependencies haven't changed for a while, so any version of nixpkgs from the last 3 months should do the trick.

Building on Windows with Cabal

  • Clone this repo with submodules (to get the C header in order to build VulkanMemoryAllocator)

    • git clone --recursive https://github.com/expipiplus1/vulkan
  • Install GHC and Cabal

    • I used ghcup
    • Make sure that the libstdc++ dll is in PATH, I had to add C:\ghcup\ghc\9.2.4\mingw\bin to my user PATH variable
  • Make sure your graphics driver has installed vulkan-1.dll in C:/windows/system32

  • Install the LunarG Vulkan SDK

    • https://vulkan.lunarg.com/sdk/home#windows
    • Remember the installation directory, use in place of C:/VulkanSDK/1.3.224.1 below
    • Install the SDL2 library/header (You can run maintenancetool in the SDK directory to install this later if you forgot)
    • We will link against vulkan-1.lib from this installation
    • We will use the glslangValidator from this installation.
  • Restart your shell to pick up the new PATH environment set up by the SDK installer The Vulkan SDK installer

  • Inform Cabal about header and library locations by adding the following to cabal.project.local, changed accodingly for your install path for the Vulkan SDK. Also use a patched SDL2 which doens't use pkgconfig (a pain to install on Windows)

    package sdl2
        extra-lib-dirs: C:/VulkanSDK/1.3.224.1/lib/
        extra-include-dirs: C:/VulkanSDK/1.3.224.1/Include/SDL2
        flags: -pkgconfig
    source-repository-package
        type: git
        location: https://github.com/expipiplus1/sdl2
        tag: 35f45303a0af522f10197f09e4bf52bc49b97ef4
    
    package vulkan
        extra-lib-dirs: C:/VulkanSDK/1.3.224.1/lib/
    
    package vulkan-utils
        extra-include-dirs: C:/VulkanSDK/1.3.224.1/Include/
    
    package VulkanMemoryAllocator
        extra-include-dirs: C:/VulkanSDK/1.3.224.1/Include/
    
    package vulkan-examples
        extra-prog-path: C:/VulkanSDK/1.3.224.1/bin
    
  • Run cabal build examples to build the examples

  • Run cabal run resize to run the resize example.

Building on Windows with Stack

Stack is currently (2020-11-02) bundled with an msys2 installation which is too old to use the package repositories (see commercialhaskell/stack#5300) so installing the Vulkan SDK, SDL2 and pkg-config is not possible with the bundled package manager.

Nevertheless, it should be possible to use Stack by adding the following to stack.yaml (changed appropriately according to SDL2 and VulkanSDK install locations) and building after following the instructions above.

extra-lib-dirs:
- C:/VulkanSDK/1.2.135.0/lib/

extra-include-dirs:
- C:/VulkanSDK/1.2.135.0/Include/
- C:/VulkanSDK/1.3.224.1/Include/SDL2

Examples

There exists a package to build some example programs in the examples directory.

Current Status

All the core Vulkan 1.0, 1.1, 1.2, and 1.3 functionality is here as well as all the extensions (except the video ones).

This is currently a 64 bit only library.

See also

The VulkanMemoryAllocator package (source in the VulkanMemoryAllocator directory) has similarly styled bindings to the Vulkan Memory Allocator library.

The vulkan-utils (source in the utils directory) includes a few utilities for writing programs using these bindings.

For an alternative take on Haskell bindings to Vulkan see the vulkan-api package. vulkan-api stores Vulkan structs in their C representation as ByteArray# whereas this library allocates structs on the stack and keeps them alive for just the lifetime of any Vulkan command call.


1: Note that you'll still have to request any required extensions for the function pointers belonging to that extension to be populated. An exception will be thrown if you try to call a function pointer which is null.

2: The exception is where the spec allows the application to pass NULL for the vector with a non-zero count. In these cases it was deemed clearer to preserve the "count" member and allow the Haskell application to pass a zero-length vector to indicate NULL.

3: https://github.com/spurious/SDL-mirror/blob/6b6170caf69b4189c9a9d14fca96e97f09bbcc41/src/video/windows/SDL_windowsvulkan.c#L50-L54

vulkan's People

Contributors

actions-bot avatar alt-romes avatar bgamari avatar corngood avatar dependabot[bot] avatar dpwiz avatar evilactually avatar expipiplus1 avatar int-e avatar lancelet avatar locallycompact avatar sheaf avatar threeoftwelve avatar tristancacqueray avatar ukari avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vulkan's Issues

GHC 8.6.3 : vector-sized needs updating to 1.2.*

Trying to get vulkan to build with 'cabal' with GHC 8.6.3 currently fails due to the dependency : "vector-sized" which is still ">= 0.1 && < 1.1". I have compiled vulkan manually with vector-sized-1.2.* & this seems to work.

Should the version dependency be updated?

Thanks.

Make bracket function naming more consistent

At the moment most bracket commands have with or cmdWith as a prefix. The exception is useCommandBuffer. This was so named because there's already a function called withCommandBuffers (notice the s).

I think it might be nicer to distinguish all the Begin/End pairs by having use as a prefix instead of with.

The alternative is having withCommandBuffer (which begins and ends a command buffer) and withCommandBuffers (which creates and destroys command buffers)

It's also slightly strange having with as a prefix (and calling these functions bracketing functions) when they no longer necessarily call bracket. I think this is probably ok though as with... is enough of an idiom in Haskell to get the point across.

CC @dpwiz

the type of updateDescriptorSets is too restrictive

updateDescriptorSets
  :: forall a io
   . (Extendss WriteDescriptorSet a, PokeChain a, MonadIO io)
  => Device
  -> ("descriptorWrites" ::: Vector (WriteDescriptorSet a))
  -> ("descriptorCopies" ::: Vector CopyDescriptorSet)
  -> io ()

It should be the following:

updateDescriptorSets
  :: forall io
   . MonadIO io
  => Device
  -> ("descriptorWrites" ::: Vector (SomeStruct WriteDescriptorSet))
  -> ("descriptorCopies" ::: Vector CopyDescriptorSet)
  -> io ()

Windows build instructions

Readme (or wiki) should document how to get dependencies (easy with stack) and extra tweaks in code to build e.g. triangle example.

Confusing docs and type of DescriptorSetLayoutBinding.immutableSamplers

I just had a day of wild goose chase after mystery descriptorCount prompted by this validation error (with a propmpt freeze afterwards):

vkUpdateDescriptorSets() failed write update validation for Descriptor Set 0x14 with error: VkDescriptorSet 0x14allocated with VkDescriptorSetLayout 0x11 cannot updated binding 0 that has 0 descriptors. The Vulkan spec states: dstBinding must be a binding with a non-zero descriptorCount

The other vulkan library has this as an explicit parameter, but here "either blindness" tricked me into thinking that either descriptor set has some immutable samplers ("If descriptorType is not one of these descriptor types, then pImmutableSamplers is ignored") or it is a some kind of placeholder.

I wish the non-usage of Maybe surprised me more here. Perhaps I've been side-tracked by Word32 and its usage for handles and stuff.

Add resource managing bindings

It would be nice to have bracket functions which used ResourceT, Managed or Polysemy.Resource to manage resources. These really shouldn't be too hard to generate, but probably shouldn't go in the main library. I guess while we're here we could also do bindings lifted into MonadIO

The procedure would be, for each module with bracket commands in to write a new module reexporting all but the bracketed commands, as well as the differently typed bracketing command.

It would also be necessary to do the parent modules too, so one could just import Graphics.Vulkan.Core10.ResourceT to get everything but the brackets from Graphics.Vulkan.Core10 as well as the new bracket functions.

Might also be nice to have a Vulkan monad which carries around the instance and device, as well as a separate monad for cmds.

Current roadmap?

Hi,
What's the current roadmap for this library? I've seen some mention of higher-level abstractions, do those exist anywhere? (Or is there at least some documentation on how those might exist somewhere?) .

Sweet, thanks

Replace VkBool32 with Bool

Seeing as VkBool32 can only ever be VK_TRUE or VK_FALSE it would be nice to use Bool instead.

It would be nice to back this up with something in the spec...

Generate safe calls for long running operations

There's no clear specification of which operations are "long running". I think that those which can return "VK_TIMEOUT" would be a good start.

This is important stopping GC for such a potentially long time while waiting on a fence or something is not great.

The marshaling of WriteDescriptorSet is incorrect

The lengths of the arrays depend on descriptorCount and also descriptorType. For some descriptor types they can all be zero.

I think length inference would work poorly here (would have to walk the struct chain for some descriptor types for example.

We should probably audit any members/parameters with noautovalidity=true for weirdness like this.

see also: #71

Missing vulkan-headers when building VulkanMemoryAllocator

Not sure how/why exactly that happens, because I can see that vulkan-headers library should be there: https://github.com/expipiplus1/vulkan/blob/master/default.nix#L134

But for some reason, it can't find the required headers:

builder for '/nix/store/g0b479rkx7i8hwa6bjrh9gzcin7jxdlw-VulkanMemoryAllocator-0.3.drv' failed with exit code 1; last 10 log lines:
  
  VulkanMemoryAllocator/src/vk_mem_alloc.h:1942:14: error:
       fatal error: vulkan/vulkan.h: No such file or directory
       1942 |     #include <vulkan/vulkan.h>
            |              ^~~~~~~~~~~~~~~~~
       |
  1942 |     #include <vulkan/vulkan.h>
       |              ^
  compilation terminated.
  `cc' failed in phase `C Compiler'. (Exit code: 1)

In my case, the fix was to add vulkan-headers to the extraLibraries by:

(pkgs.haskell.lib.addExtraLibrary VulkanMemoryAllocator pkgs.vulkan-headers)

Validation layers spam logs with VMA-induced warning

Due to unfixable warning in VMA every buffer update in the pool having an Image somewhere would emit a validation message:

Mapping an image with layout VK_IMAGE_LAYOUT_something_something can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.

https://www.reddit.com/r/vulkan/comments/ekx8c4/suppress_vma_warnings_in_validation_layers/

This results in a message emitted every frame, flooding the output and botching FPS.

Debug handler in vulkan-utils could use some fast warning suppression mechanism.

High level bindings ideas

A dump of ideas for higher level bindings:

  • In the spec parameters are marked as optional or not, it would be nicer to use Maybe for these rather than null pointer/zero values.
  • Passing values in via const pointers is a pain, it shouldn't be that hard at all to write wrappers for all the commands. The wrapper would take a Haskell struct and do the alloca dance for the user. The spec suggests that this is a safe thing to do for all the const pointers used in the spec.
  • In correspondence with the above idea, getting values out my passing non-const pointers to parameters is a pain too, the wrappers could perform the allocation and peek and return all the outputs in a tuple.
  • Storing sType in all the records is a bit silly as they can only have one possible value. Should this even be removed from the low level bindings?
  • Struct chains shouldn't be that hard to implement in Haskell as some kind of vinyl like heterogeneous list.
  • Pairs of functions (vkCreateXXX/vkDestroyXXX, vkAllocateXXX/vkFreeXXX) could be made into a withXXX function which makes sure that destruction takes place with bracket.

Bikeshedding:

  • Should this be in a new library (vulkan-easy/vulkan-wrappers or something) or in this library Graphics.Vulkan.Easy....

Howto write shader input uniform value

Would it be possible to add some documentations and/or a function to update an uniform value? If I understand correctly the code in resize/Main.hs, the Julia shader Frame uniform is updated using:

          allocaBytes constantBytes $ \p -> do
            liftIO $ poke (p `plusPtr` 0) frameScale
            liftIO $ poke (p `plusPtr` 8) frameOffset
            liftIO $ poke (p `plusPtr` 16) c
            liftIO $ poke (p `plusPtr` 24) escapeRadius
            cmdPushConstants' fJuliaPipelineLayout
                              SHADER_STAGE_COMPUTE_BIT
                              0
                              constantBytes
                              p

I'm looking for replacing opengl (example here), and I am not sure what is the vulkan equivalent of Graphics.GL.Core31.glUniform2f.

Thank you!

issues building examples on master branch

I'm trying to build the examples as detailed in readme.md, but I'm running into an error.

First- I'm assuming the ns -p is an alias for nix-shell -p, so i'm running this:

nix-shell -p stack ghc vulkan-loader vulkan-headers pkg-config SDL2 --run 'stack --system-ghc build --flag vulkan:build-examples'

And getting the following error:

Specified file "VulkanMemoryAllocator/src/vk_mem_alloc.h" for extra-source-files does not exist
Stack has not been tested with GHC versions above 8.6, and using 8.8.2, this may fail
Stack has not been tested with Cabal versions above 2.4, but version 3.0.1.0 was found, this may fail

Error: While constructing the build plan, the following exceptions were encountered:

In the dependencies for vulkan-3.1.0.0(+build-examples):
    glslang needed, but the stack configuration has no specified version (no package with that name found, perhaps there is a typo in a package's build-depends or an omission from the stack.yaml
            packages list?)
needed since vulkan is a build target.

Some different approaches to resolving this:


Plan construction failed.

I'm running arch linux, and I have the "glslang" package installed, but I'm not sure what stack is looking for here exactly.

Lastly the readme says this:

You'll need to build the shaders first with (cd examples/sdl-triangle && glslangValidator -V shader.*)

but that pattern seems to match no files- is the readme out of date?

(FWIW: I got the Info example running fine in a standalone project!)

p.s. Neat project, thanks for sharing it!

Question: `withAsyncBound` and SDL

I've been working on getting the examples to run on macOS under MoltenVK (using the standard macOS Vulkan SDK). I have a question about withAsyncBound.

Before I started tinkering, the status of the examples (checked means working) on macOS is:

  • compute
  • info
  • offscreen
  • resize
  • sdl-triangle

I've made some changes necessary to get the sdl-triangle example working in this commit: lancelet@255baa7
A couple of the changes just involve using Vulkan 1.0 API calls (since that seems to be all that MoltenVK supports; anything else causes a segfault).

However, I can't get the example to work using withAsyncBound. If I leave that in, then during the initial creation of the window, I see this from SDL:

Assertion failed: (NSViewIsCurrentlyBuildingLayerTreeForDisplay() != currentlyBuildingLayerTree), function NSViewSetCurrentlyBuildingLayerTreeForDisplay, file /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1894.40.150/AppKit.subproj/NSView.m, line 13568.

If I push withAsyncBound down so that it only includes the main application loop then the window is initialized correctly but the main loop produces:

2020-05-24 20:07:11.771 sdl-triangle[57651:897931] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'nextEventMatchingMask should only be called from the Main Thread!'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff31d34d07 __exceptionPreprocess + 250
	1   libobjc.A.dylib                     0x00007fff6a8bf5bf objc_exception_throw + 48
	2   AppKit                              0x00007fff2ef34fd5 +[NSEvent _discardTrackingAndCursorEventsIfNeeded] + 0
	3   libSDL2-2.0.0.dylib                 0x0000000101e054ca Cocoa_PumpEvents + 138
	4   libSDL2-2.0.0.dylib                 0x0000000101d5271b SDL_WaitEventTimeout_REAL + 203
	5   sdl-triangle                        0x0000000101ad2062 sdl2zm2zi5zi2zi0zmB5N2QlaVtRD8YE5cKxrWAf_SDLziEvent_pollEvent1_info + 114
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Is there a reason to require withAsyncBound? I couldn't see an example in the Haskell SDL2 repository that uses this function, although there are numerous examples (see here for one) that call pollEvents without using it. If it is necessary, is there any special magic to get it to work on macOS?

Derive Generic

Especially considering how boilerplatey code using Vulkan typically is, it seems like it might be pretty useful for all these large records and such to derive Generic.

(The recent flurry of activity in this project is incredible. Keep up the great work!)

High level bindings?

Have you put any thought into high level bindings? Perhaps a hand written one to build on the mechanically generated low level bindings, sort of like OpenGL vs OpenGLRaw?

I started sketching something out. It's going well, but I'm now starting to deal with things that will need a lot of boilerplate, like VkFormat, which I'd rather didn't end up like this. TemplateHaskell can't deal with the pattern synonyms in Graphics.Vulkan.Core, and going back to the xml spec seems like a bad idea.

VK_INCOMPATIBLE_DRIVER_ERROR on nixos

Hi, trying to run the vulkan-example on nixos seems to result in a VK_INCOMPATIBLE_DRIVER_ERROR. My vulkaninfo seems to return good data, so I'm not sure what the issue is exactly.

resize example crashes on acquireNextImageKHR'

With the unconditional SUCCESS removed everything runs.

diff --git a/examples/resize/Main.hs b/examples/resize/Main.hs
index 854e7f27..884f0eeb 100644
--- a/examples/resize/Main.hs
+++ b/examples/resize/Main.hs
@@ -229,10 +229,18 @@ draw :: F (Fence, ())
 draw = do
   Frame {..}            <- askFrame
 
-  (SUCCESS, imageIndex) <- acquireNextImageKHR' fSwapchain
+  (res, imageIndex) <- acquireNextImageKHR' fSwapchain
                                                 0
                                                 fImageAvailableSemaphore
                                                 zero
+  case res of
+    SUCCESS ->
+      pure ()
+    NOT_READY ->
+      sayErrString $ show res
+    err ->
+      error $ show err
+
   let image = fImages imageIndex
   let imageSubresourceRange = ImageSubresourceRange
         { aspectMask     = IMAGE_ASPECT_COLOR_BIT

But then there are some validation errors for a few first frames:

NOT_READY
Validation: Queue 0x7f9c9c088920 is waiting on semaphore 0x26 that has no way to be signaled.
Validation: vkQueuePresentKHR: Swapchain image index 0 has not been acquired.
NOT_READY
Validation: vkUpdateDescriptorSets() failed write update validation for Descriptor Set 0x1e with error: Cannot call vkUpdateDescriptorSets() to perform write update on VkDescriptorSet 0x1eallocated with VkDescriptorSetLayout 0x1c that is in use by a command buffer. The Vulkan spec states: All submitted commands that refer to any element of pDescriptorSets must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkFreeDescriptorSets-pDescriptorSets-00309)
Validation: Queue 0x7f9c9c088920 is waiting on semaphore 0x26 that has no way to be signaled.
Validation: vkQueuePresentKHR: Swapchain image index 0 has not been acquired.
NOT_READY
Validation: vkUpdateDescriptorSets() failed write update validation for Descriptor Set 0x1e with error: Cannot call vkUpdateDescriptorSets() to perform write update on VkDescriptorSet 0x1eallocated with VkDescriptorSetLayout 0x1c that is in use by a command buffer. The Vulkan spec states: All submitted commands that refer to any element of pDescriptorSets must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkFreeDescriptorSets-pDescriptorSets-00309)
Validation: Queue 0x7f9c9c088920 is waiting on semaphore 0x26 that has no way to be signaled.
Validation: vkQueuePresentKHR: Swapchain image index 0 has not been acquired.
NOT_READY
Validation: vkUpdateDescriptorSets() failed write update validation for Descriptor Set 0x1e with error: Cannot call vkUpdateDescriptorSets() to perform write update on VkDescriptorSet 0x1eallocated with VkDescriptorSetLayout 0x1c that is in use by a command buffer. The Vulkan spec states: All submitted commands that refer to any element of pDescriptorSets must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkFreeDescriptorSets-pDescriptorSets-00309)
Validation: Queue 0x7f9c9c088920 is waiting on semaphore 0x26 that has no way to be signaled.
Validation: vkQueuePresentKHR: Swapchain image index 0 has not been acquired.
NOT_READY
Validation: vkUpdateDescriptorSets() failed write update validation for Descriptor Set 0x1e with error: Cannot call vkUpdateDescriptorSets() to perform write update on VkDescriptorSet 0x1eallocated with VkDescriptorSetLayout 0x1c that is in use by a command buffer. The Vulkan spec states: All submitted commands that refer to any element of pDescriptorSets must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkFreeDescriptorSets-pDescriptorSets-00309)
Validation: Queue 0x7f9c9c088920 is waiting on semaphore 0x26 that has no way to be signaled.
Validation: vkQueuePresentKHR: Swapchain image index 0 has not been acquired.
NOT_READY
Validation: vkUpdateDescriptorSets() failed write update validation for Descriptor Set 0x1e with error: Cannot call vkUpdateDescriptorSets() to perform write update on VkDescriptorSet 0x1eallocated with VkDescriptorSetLayout 0x1c that is in use by a command buffer. The Vulkan spec states: All submitted commands that refer to any element of pDescriptorSets must have completed execution (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkFreeDescriptorSets-pDescriptorSets-00309)
Validation: Queue 0x7f9c9c088920 is waiting on semaphore 0x26 that has no way to be signaled.
Validation: vkQueuePresentKHR: Swapchain image index 0 has not been acquired.
9.292408ms      56%
11.550242ms     70%
9.010824ms      55%
10.557957ms     64%
9.130374ms      55%
8.577337ms      52%
...

Building with nix

Hi, what is the intended way to build using the default.nix files in the library? If I do a nix-build or nix-shell from the root it seems to attempt to build the whole of hackage.

Many foreign imports make sense to be unsafe imports

A lot of Vulkan functions deserve unsafe bindings, for efficiency. I don't know if the spec provides enough information that the generator could figure out which ones to make unsafe automatically. Automatic or not, there are certainly some gotchas, and these are just the ones I can think of right now:

  • Some functions accept callbacks. These should have safe bindings. For those with optional callbacks, perhaps there could also be unsafe bindings, exposed to the user through wrapper functions that don't accept the callbacks.
  • I don't know the spec very well yet, but unless the spec forbids it, I can imagine that Vulkan might store a callback to use later. This possibility could make it difficult to tell from a function's signature whether it might call back into Haskell.
  • Some functions can block. These would need to be identified.

Non-WSI extensions are are broken

e.g. EXT_debug_report

Only the core APIs and WSI extensions actually have symbols provided in the loader. Everything else needs to be dynamically loaded via Get[...]ProcAddr.

I threw something together as a proof of concept, which gets the extension working for me: @93cea16 (WIP).

What do you think of this approach? It might even make sense to fully transistion to dynamic loading, using e.g. dlopen to get vkGetInstanceProcAddr, and grabbing everything else from there. However in that case you'd need to handle failure, which would probably rule out the unsafePerformIO.

With this change users would be encouraged reuse command functions after partially applying them on instance / device, which is equivalent to good practice in C.

Rendering to video

Hi, is there a way to take a vulkan haskell program and render it to image/video headlessly in CI?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.