vulkan: Bindings to the Vulkan graphics API.

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

Warnings:


[Skip to Readme]

Properties

Versions 0.1.0.0, 0.2.0.0, 1.0.0.0, 1.2.0.0, 1.3.0.0, 1.3.1.0, 1.3.2.0, 1.5.0.0, 1.5.1.0, 1.6.0.0, 1.7.0.0, 2.0.0.0, 2.0.0.1, 2.1.0.0, 3.0.0.0, 3.1.0.0, 3.2.0.0, 3.3, 3.3.1, 3.4, 3.5, 3.6, 3.6.1, 3.6.2, 3.6.2, 3.6.3, 3.6.4, 3.6.5, 3.6.6, 3.6.7, 3.6.8, 3.6.9, 3.6.10, 3.6.11, 3.6.11.1, 3.6.12, 3.6.13, 3.6.14, 3.6.15, 3.7, 3.8, 3.8.1, 3.8.2, 3.8.3, 3.9, 3.9.1, 3.10, 3.10.1, 3.10.2, 3.10.3, 3.10.4, 3.11, 3.11.0.2, 3.11.1, 3.11.2, 3.11.3, 3.11.4, 3.11.4.1, 3.11.5, 3.12, 3.12.1, 3.12.2, 3.13, 3.13.1, 3.13.2, 3.13.3, 3.13.4, 3.14, 3.14.1, 3.14.2, 3.15, 3.16, 3.16.1, 3.16.2, 3.17, 3.21, 3.21.1, 3.22, 3.22.1, 3.23, 3.23.1, 3.23.2, 3.23.3, 3.23.4, 3.24, 3.24.1, 3.24.2, 3.24.3, 3.24.4, 3.24.5, 3.25, 3.26, 3.26.1, 3.26.2
Change log changelog.md
Dependencies base (<4.15), bytestring, transformers, vector [details]
License BSD-3-Clause
Author
Maintainer Joe Hermaszewski <live.long.and.prosper@monoid.al>
Category Graphics
Home page https://github.com/expipiplus1/vulkan#readme
Bug tracker https://github.com/expipiplus1/vulkan/issues
Source repo head: git clone https://github.com/expipiplus1/vulkan
Uploaded by jophish at 2020-07-21T05:38:20Z

Modules

[Index] [Quick Jump]

Flags

Manual Flags

NameDescriptionDefault
generic-instances

Derive Generic instances for all structs. Disabled by default because of code size and compile time impact.

Disabled
safe-foreign-calls

Do not mark foreign imports as unsafe. This means that callbacks from Vulkan to Haskell will work. If you are using these then make sure this flag is enabled.

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for vulkan-3.6.2

[back to package description]

vulkan

Slightly high level Haskell bindings to the Vulkan graphics API.

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:

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

Minor things

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.

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.

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

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:

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:

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 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

Examples

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

Current Status

All the core Vulkan 1.0, 1.1, and 1.2 functionality is here as well as all the extensions.

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 package (not currently on Hackage) 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.