Coder Social home page Coder Social logo

achirkin / vulkan Goto Github PK

View Code? Open in Web Editor NEW
63.0 6.0 12.0 5.99 MB

Low-level low-overhead haskell bindings to vulkan API

License: BSD 3-Clause "New" or "Revised" License

Haskell 99.80% C 0.02% GLSL 0.02% Shell 0.01% Nix 0.15%
vulkan vulkan-api graphics haskell haskell-library

vulkan's Introduction

The aim of this project is to provide low-level low-overhead haskell bindings to vulkan api. Features of the bindings:

  • Keep as close as possible to vulkan naming conventions unless they violate haskell syntax. This involves heavy usage of PatternSynonyms extension, and occasional violation of camel case.
  • Provide as much as possible information at type level, but allow avoiding any overheads related to it. Compile-time constants are duplicated at type level, but it is not necessary to use them.
  • Do not introduce type marshalling overheads. All vulkan structures have ByteArray# runtime representation, allowing zero-copy conversion to and from pointers. Moreover, it is not necessary to convert them at all, if one prefers to manage corresponding memory manually.
  • Use no dependencies except base.

vulkan-api Hackage

Generated haskell bindings for vulkan api.

  • The generated library is rather big; consider using -split-objs or -split-sections to reduce the size of a project. Note, enabling one of these options can make the library compiling painfully long time (take some coffee... or watch a movie).

  • By default, the library loads vulkan symbols explicitly dynamically at runtime. Therefore, it does not even link to the vulkan loader library at compile time.

  • The library provides useNativeFFI-x-y flags that enable haskell FFI functions for vulkan core x.y symbols. Turning on any of these flags enables compile-time linking to the vulkan loader library.

  • All available extension functions can be found at runtime using simple lookup functions in Graphics.Vulkan.Marshal.Proc module.

Tested using stack on:

Status update vulan-api-1.4 (2021.04.05)

Vulkan-Docs changed between version 1.1 and 1.2 a lot, which made adapting genvulkan rather hard. At this point, I decided to modify the generated code manually until I come up with a better way to generate haskell code fully automatically (I expect this would require a rather large refactoring).

The current semi-generated code matches v. 1.2.174 of Vulkan-Docs vk.xml. Here are some manual adjustments I've had to make:

  • VkAccelerationStructureInstanceKHR has bitfields and not processed by hsc2hs and does not fit VulkanMarshal.StructRep; the manual class instance workarounds this (rather inconveniently).
  • A few new cyclic module dependencies must have been fixed with manual .hs-boot
  • Graphics.Vulkan.Ext.VK_NV_ray_tracing and some related structs are hidden behind enableBetaExtensions flag (seems to compile with the flag enabled though)

genvulkan

Generate haskell vulkan sources using vk.xml file. To update the api bindings, run genvulkan using stack with this project folder:

cd genvulkan
stack build
stack exec genvulkan

vulkan-examples

Examples of programs using vulkan-api bindings. Consists of several executables implementing steps of vulkan-tutorial.com. This is the easiest way to familiarize yourself with the library.

Prerequisites

  • For validation layers to work, you need to have Vulkan SDK installed, get it on vulkan.lunarg.com.
  • Some examples compile shaders using glslangValidator via TH, so the tool must be in your PATH (it is included in Vulkan SDK).
  • Windowing is done via GLFW, so you may need to have it on your system, version 3.2 or newer.

vulkan-triangles

A more haskell-style example of a vulkan program. This is a combined result of programs in vulkan-examples with a little cleaner code.

TODO

vulkan-api
  • Try to build it on various platforms, check if specifying foreign code calling convention is necessary.
  • Remove unsafe FFI call to functions that could break at runtime. Currently we have both safe and unsafe versions for every function.
  • Figure out if it is necessary to have extra-libraries: vulkan on various platforms (or, maybe, extra-ghci-libraries is enough?). An alternative would be to make a C stub to get all functions via vk***ProcAddr, which seems not the best option due to performance considerations of doing dynamic wrapping FFI.
  • Make Graphics.Vulkan.Marshal.Create fill sType fields automatically, together with optional fields
  • Make Graphics.Vulkan.Marshal.Create provide more meaningful error messages when types of fields mismatch.
  • Check whether we can disallow writing returnedonly fields.
  • Investigate the need to use the extension loader (vulkan-docs/src/ext_loader). Graphics.Vulkan.Marshal.Proc seems to be good enough for this low-level binding.
  • vkGetProc and vkLookupProc currently lookup functions in a shared library, even if vulkan is linked statically. This can be dangerous! Need to check it.
genvulkan
  • VkXml.Sections.Commands: parse command parameters more robustly, maybe use language-c package for that. Make parsing more compliant with the registry spec.
  • VkXml.Sections.Types parseVkTypeData needs a cleaner rewrite. Especially, check if type and member names are parsed correctly.

Why another Haskell bindings?

The generated bindings vulkan-api are not the only Haskell bindings for Vulkan API. There is another package, called vulkan that started in 2016. The main reason for me to write this new package two years later was that vulkan package was abandoned for a while and required significant efforts to be compiled at the time this project started in January 2018 (as of April 2018 things seem to have changed and that package is great again :) ). However, the are a few design decisions that render vulkan and vulkan-api quite different. The main difference is that vulkan uses regular Haskell data types plus DuplicateRecordFields to manipulate Vulkan objects, whereas vulkan-api uses wrapped pinned byte arrays plus type classes and TypeApplications; as a result:

  • Creating and composing data types in vulkan is very close to normal haskell way of doing that (modulo the need to manually allocate pointers). Creating and composing data types in vulkan-api is done via VulkanMarshal class. There are helpers for managing memory in Graphics.Vulkan.Marshal.Create module, you can find some examples in the repository.

  • Duplicate field names in vulkan structure, such as sType use DuplicateRecordFields and often require you writing a lot of type signatures explicitly, which can be very annoying. Things will become better with record type inference and OverloadedRecordFields extension; but this is not implemented even in GHC 8.4 yet.

  • Writing structure fields in vulkan-api is done via type classes (and heavy inlining); thus, overloading with custom data types is extremely easy (e.g. writing vectors or bytearrays directly into vulkan structures). That comes at the cost of a not particularly novice-friendly interface.

  • Low overheads: vulkan-api structures can be converted to and from C pointers for FFI doing zero copying. There is no need to peek all fields of a structure to read one of them.

There is a number of smaller things:

  • vulkan-api has different vkGetXxxProc machinery for loading Vulkan symbols dynamically, check out Graphics.Vulkan.Marshal.Proc for that.

  • vulkan-api keeps all Vulkan extension names in Ptr CString bi-directional patterns, which eliminates the need to alloca when feeding them to Vulkan functions.

  • Most of the constants in vulkan-api are duplicated at type level using Nat and Symbol, which should allow more type-level programming and fancy high-level wrapppers.

vulkan's People

Contributors

achirkin avatar cjay avatar locallycompact avatar o1lo01ol1o avatar rotaerk avatar sheaf avatar ubuntunux 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

vulkan's Issues

MoltenVK on High Sierra: GLFW reports that vulkan is not supported!

The readme states that vulkan-api was tested on MoltenVK. How was GLFW linked to vulkan? It looks like GLFW-b packages a version of glfw the potentially predates MoltenVK support. I'm abled to install glfw (via source) and am told that I finds the vulkan libraries. However, it looks like stack build in vulkan-examples links the wrong glfw:

stack exec ve-06-Drawing
GLFW version: 3.2.1 Cocoa NSGL chdir menubar retina
Terminated GLFW.
ve-06-Drawing: VulkanException {vkeCode = Nothing, vkeMessage = "GLFW reports that vulkan is not supported!"}

Core 1.1 *_BIT patterns of wrong type

I've been working with Vulkan 1.0 this whole time, but I decided to import Core_1_1 to make use of VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT. However, I get a type error when attempting to combine it with VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT from 1.0.

Looking at the source, it appears their types are different, and I suspect this is a mistake.

VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT :: VkImageCreateBitmask a

vs

VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT :: VkImageCreateFlagBits

Reducing the size of the project

With last few versions of vulkan-api I've got some trouble: the generated haddock documentation was above 50MB and hackage rejected it for uploading.
This led me to thing that the source code size (not only haddock) is a bit too much. I open this issue as a request to check what things are not used and can be dropped. This is especially a question to @cjay , @o1lo01ol1o , and @Rotaerk .

For starters, I am thinking about reducing the deriving clause for generated enums: remove Num, Bounded, Data, and Generic instances. None of these seem to be useful, and Bounded actually is misleading (e.g. max bound it that of Int32 and does not correspond to the actual enum range). Shall I do that?

For Bits and Bitmasks I would remove Bounded, Data, and Generic, and I am not sure about Real, Num, and Integral. Am I missing anything?

With structs it is a bit more complicated. I think it is possible to restructure a few things in VulkanMarshal such that Eq, Show, Ord, Storable can be expressed once in terms of it, and all Field-related classes can completely go away. The problem is that it probably won't be possible to define VulkanMarshal instance for user types. Is that a problem?

Old resolvers causing issue

The resolver in vulkan-examples can be updated to lts-12.11, in my testing on Windows all the examples work fine except 5, which fails with this error:

VUID-VkPipelineVertexInputStateCreateInfo-pVertexAttributeDescriptions-parameter(ERROR / SPEC): msgNum: 279077889 - vkCreateGraphicsPipelines: required parameter pCreateInfos[0].pVertexInputState->pVertexAttributeDescriptions specified as NULL. The spec valid usage text states 'If vertexAttributeDescriptionCount is not 0, pVertexAttributeDescriptions must be a valid pointer to an array of vertexAttributeDescriptionCount valid VkVertexInputAttributeDescription structures' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkPipelineVertexInputStateCreateInfo-pVertexAttributeDescriptions-parameter)
    Objects: 1
       [0] 0x0, type: 0, name: (null)
Validation(ERROR): msg_code: 279077889:  [ VUID-VkPipelineVertexInputStateCreateInfo-pVertexAttributeDescriptions-parameter ]  [ VUID-VkPipelineVertexInputStateCreateInfo-pVertexAttributeDescriptions-parameter ] Object: VK_NULL_HANDLE (Type = 0) | vkCreateGraphicsPipelines: required parameter pCreateInfos[0].pVertexInputState->pVertexAttributeDescriptions specified as NULL. The spec valid usage text states 'If vertexAttributeDescriptionCount is not 0, pVertexAttributeDescriptions must be a valid pointer to an array of vertexAttributeDescriptionCount valid VkVertexInputAttributeDescription structures' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkPipelineVertexInputStateCreateInfo-pVertexAttributeDescriptions-parameter)
VUID-VkPipelineVertexInputStateCreateInfo-vertexAttributeDescriptionCount-00614(ERROR / SPEC): msgNum: 278922444 - vkCreateGraphicsPipelines: pararameter pCreateInfo[0].pVertexInputState->vertexAttributeDescriptionCount (0) is greater than VkPhysicalDeviceLimits::maxVertexInputAttributes (16). The spec valid usage text states 'vertexAttributeDescriptionCount must be less than or equal to VkPhysicalDeviceLimits::maxVertexInputAttributes' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkPipelineVertexInputStateCreateInfo-vertexAttributeDescriptionCount-00614)
    Objects: 1
       [0] 0x0, type: 0, name: (null)
Validation(ERROR): msg_code: 278922444:  [ VUID-VkPipelineVertexInputStateCreateInfo-vertexAttributeDescriptionCount-00614 ]  [ VUID-VkPipelineVertexInputStateCreateInfo-vertexAttributeDescriptionCount-00614 ] Object: VK_NULL_HANDLE (Type = 0) | vkCreateGraphicsPipelines: pararameter pCreateInfo[0].pVertexInputState->vertexAttributeDescriptionCount (0) is greater than VkPhysicalDeviceLimits::maxVertexInputAttributes (16). The spec valid usage text states 'vertexAttributeDescriptionCount must be less than or equal to VkPhysicalDeviceLimits::maxVertexInputAttributes' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkPipelineVertexInputStateCreateInfo-vertexAttributeDescriptionCount-00614)

With the old resolver, I could not get vulkan-examples to compile.

I can't get vulkan-triangles to work with the resolver it had (nightly-2018-04-12) or with lts-12.11. In both cases I get the following error:

vulkan-api-1.1.3.0: configure (lib)
Progress 1/3

--  While building custom Setup.hs for package vulkan-api-1.1.3.0 using:
      C:\sr\setup-exe-cache\x86_64-windows\Cabal-simple_Z6RU0evB_2.2.0.1_ghc-8.4.3.exe --builddir=.stack-work\dist\7d103d30 configure --with-ghc=C:\Users\hyper\AppData\Local\Programs\stack\x86_64-windows\ghc-8.4.3\bin\ghc.EXE --with-ghc-pkg=C:\Users\hyper\AppData\Local\Programs\stack\x86_64-windows\ghc-8.4.3\bin\ghc-pkg.EXE --user --package-db=clear --package-db=global --package-db=C:\sr\snapshots\bbeb756e\pkgdb --package-db=C:\Users\hyper\Desktop\vulkan-master\vulkan-triangles\.stack-work\install\96fd7a97\pkgdb --libdir=C:\Users\hyper\Desktop\vulkan-master\vulkan-triangles\.stack-work\install\96fd7a97\lib --bindir=C:\Users\hyper\Desktop\vulkan-master\vulkan-triangles\.stack-work\install\96fd7a97\bin --datadir=C:\Users\hyper\Desktop\vulkan-master\vulkan-triangles\.stack-work\install\96fd7a97\share --libexecdir=C:\Users\hyper\Desktop\vulkan-master\vulkan-triangles\.stack-work\install\96fd7a97\libexec --sysconfdir=C:\Users\hyper\Desktop\vulkan-master\vulkan-triangles\.stack-work\install\96fd7a97\etc --docdir=C:\Users\hyper\Desktop\vulkan-master\vulkan-triangles\.stack-work\install\96fd7a97\doc\vulkan-api-1.1.3.0 --htmldir=C:\Users\hyper\Desktop\vulkan-master\vulkan-triangles\.stack-work\install\96fd7a97\doc\vulkan-api-1.1.3.0 --haddockdir=C:\Users\hyper\Desktop\vulkan-master\vulkan-triangles\.stack-work\install\96fd7a97\doc\vulkan-api-1.1.3.0 --dependency=base=base-4.11.1.0 -fuseNativeFFI-1-0 -f-useNativeFFI-1-1 -fuseUnsafeFFIDefault --ghc-options -O2 --extra-include-dirs=C:\Users\hyper\AppData\Local\Programs\stack\x86_64-windows\msys2-20180531\mingw64\include --extra-lib-dirs=C:\Users\hyper\AppData\Local\Programs\stack\x86_64-windows\msys2-20180531\mingw64\bin --extra-lib-dirs=C:\Users\hyper\AppData\Local\Programs\stack\x86_64-windows\msys2-20180531\mingw64\lib --enable-tests --enable-benchmarks
    Process exited with code: ExitFailure 1
    Logs have been written to: C:\Users\hyper\Desktop\vulkan-master\vulkan-triangles\.stack-work\logs\vulkan-api-1.1.3.0.log

    Configuring vulkan-api-1.1.3.0...
    Cabal-simple_Z6RU0evB_2.2.0.1_ghc-8.4.3.exe: Missing dependency on a foreign
    library:
    * Missing (or bad) C library: vulkan-1
    This problem can usually be solved by installing the system package that
    provides this library (you may need the "-dev" version). If the library is
    already installed but in a non-standard location then you can use the flags
    --extra-include-dirs= and --extra-lib-dirs= to specify where it is.If the
    library file does exist, it may contain errors that are caught by the C
    compiler at the preprocessing stage. In this case you can re-run configure
    with the verbosity flag -v3 to see the error messages.

Failed to initialize GLFW on nixos

Hi, I'm getting these errors on the GLFW examples running on nixos

lc@icecrown ~/v/vulkan-examples> stack --nix exec -- ve-02-GLFWWindow
No protocol specified
ve-02-GLFWWindow: VulkanException {vkeCode = Nothing, vkeMessage = "Failed to initialize GLFW."}

Add explicitly bidirectional pattern synonyms for certain structs

Instead of always using getField and set, I believe some non-opaque struct types could benefit from using explicitly bidirectional pattern synonyms. For instance:

{-# COMPLETE VkSurfaceFormatKHR #-}
pattern VkSurfaceFormatKHR :: VkFormat -> VkColorSpaceKHR -> VkSurfaceFormatKHR
pattern VkSurfaceFormatKHR fmt spc
  <- ( getField @"format" &&& getField @"colorSpace"-> (fmt, spc) )
    where VkSurfaceFormatKHR fmt spc
            = createVk ( set @"format" fmt &* set @"colorSpace" spc )

Are all the types really needed?

Disclaimer: I've only been figuring out Vulkan for two days flat, so I've just barely passed creating logical devices in the API reference, but I do have experience porting quite a few C libraries into Haskell.

So far the library works fine, except for one tiny issue: the compilation times are abysmal.

To confirm that this isn't just me being dumb with the code on my end I created a test that looks like

wrap :: forall (a :: Type) (fname :: Symbol). CanWriteField fname a => IO ()     
wrap = writeField @fname @a nullPtr undefined    
    
throwaway :: IO ()    
throwaway    
  | () == ()  = return ()    
  | otherwise = do    
      wrap @VkApplicationInfo @"sType"
      wrap @VkApplicationInfo @"pNext"                 
      wrap @VkApplicationInfo @"pApplicationName"
      ...

The wraps go on for 220 more lines to include every field in VkApplicationInfo, VkInstanceCreateInfo, VkPhysicalDeviceLimits, VkPhysicalDeviceSparseProperties, VkPhysicalDeviceProperties, VkPhysicalDeviceFeatures and VkDeviceCreateInfo. The compilation time of this file at -O1 -Wall is 30 seconds on my machine.

The code in this library depends on a lot of typing and type families are incredibly slow on recursion (#8095), my question therefore is:

Is all the type-level fluff actually needed or can this be removed?

So far I've been interacting with the library solely through writeField/readField (fieldOffset) and raw Ptrs. In my experience this set of features is all that is needed to operate C binding libraries, anything beyond that is overdesigning. The programmer will have to define multiple modules of wrappers anyway, so limiting them in any capacity at best yields neglible benefits to those who fully agree on your API design and at worst renders the library unusable.

The rewrite as I see it would be:

  • Complete removal of VkStruct and all the complex type code. Use GHC.Records.HasField for field name definition and a custom class for offsets (I use storable-offset for this). This is the important change, every other one below is simply my preference and is most probably inconsequential;
  • Complete removal of type-level extension names and the like. There is no reason to have them at the type-level when you can (and do) export Strings and/or CStrings;
  • Adding macro support through CApiFFI. VK_MAKE_API_VERSION and the like should definitely be parts of this library;
  • Removing most instances. Show, Eq, Ord, Read and Enum serve little purpose; Enum and Show in particular are not total for enumerations ever;
  • Pattern synonyms can most probably be relaxed to work forall a. (Num a, Eq a) => a. The typing doesn't help much here.

I am not in the mood to create a third Vulkan bindings library, however I also recognize that removing all the overtyping of this library is an immediate bump to v2.0.0.0 with all the rewrite baggage for the end users.

cabal flags that toggle visibility of modules

I was asking something in the freenode #haskell channel, and linked this: https://github.com/achirkin/vulkan/blob/master/vulkan-api/vulkan-api.cabal. Someone spoke up and pointed out that it's "Not Okay" for flags to affect the publicly visible API. In particular, you have a bunch of flags that toggle the visibility of certain modules.

I asked for an alternative solution, and the suggestions were to do one of the following:

  1. Always expose those modules.
  2. Break vulkan-api into multiple packages, so that each group of modules that you're conditionally exposing get moved to a different package altogether.

Also, I haven't checked if it's being done anywhere, but I imagine using flags to enable CPP options that, in-turn, cause parts of the public API to be toggled on and off would also be a bad thing for the same reason.

As I understand it, the main reason for this is that you don't want whether a flag is enabled to impact the code of a dependent package, because you can't make a cabal package depend on a specific flag configuration of another project. On the other hand, flags that improve performance (such as useNativeFFI-*) are fine, because they don't impact the implementation of dependent packages.

Idea for new 'setCountAndListRef' Create.hs function

I've noticed a lot of structures in which you have to set a count field and a pointer field, and it often ends up looking like this:

set @"enabledExtensionCount" (lengthNum extensions) &*
setListRef @"ppEnabledExtensionNames" extensions

where:

lengthNum :: (Foldable t, Num n) => t a -> n
lengthNum = fromIntegral . length
{-# INLINE lengthNum #-}

I'm now imagining a helper for this pattern, something that looks like this:

setCountAndListRef @"enabledExtensionCount" @"ppEnabledExtensionNames" extensions

Best I can come up with to implement it is this:

setCountAndListRef ::
  forall countFname arrayFname x c s.
  (
    CanWriteField countFname x,
    CanWriteField arrayFname x,
    FieldType countFname x ~ c,
    FieldType arrayFname x ~ Ptr s,
    Storable s,
    Num c
  ) =>
  [s] -> CreateVkStruct x '[countFname, arrayFname] ()
setCountAndListRef ss =
  set @countFname (lengthNum ss) &*
  setListRef @arrayFname ss

But it doesn't type-check, and I'm unsure how to resolve it. It complains that it can't deduce Graphics.Vulkan.Marshal.Create.Union x '[countFname] '[arrayFname] ~ '[countFname, arrayFname].

Edit: Maybe a better name would be setListCountAndRef.

long compilation time for one line of code

Not sure if this is working as intended or some bug so i post it here.

This one line takes about a minute to compile:

deviceFeaturesStruct = createVk @VkPhysicalDeviceFeatures @'[] $ pure ()

LTS-15.15 stack,
extra-deps:

  • vulkan-api-1.3.0.0@sha256:9f905a1656c35cf15786a1f2669ab83b8f4633ca35b2affcbb05012958ab12ce,19705

Getting a pointer to an array of VulkanMarshal values

Slowly making my way through the vulkan tutorial, I've encountered a lot of creation functions that fit this pattern:

newVkInstance :: VkInstanceCreateInfo -> Acquire VkInstance
newVkInstance createInfo =
  (
    withPtr createInfo $ \createInfoPtr ->
      alloca $ \vulkanInstancePtr -> do
        vkCreateInstance createInfoPtr VK_NULL vulkanInstancePtr &
          onVkFailureThrow "vkCreateInstance failed."
        peek vulkanInstancePtr
  )
  `mkAcquire`
  \vulkanInstance -> vkDestroyInstance vulkanInstance VK_NULL

newVkDevice :: VkPhysicalDevice -> VkDeviceCreateInfo -> Acquire VkDevice
newVkDevice physicalDevice createInfo =
  (
    withPtr createInfo $ \createInfoPtr ->
      alloca $ \devicePtr -> do
        vkCreateDevice physicalDevice createInfoPtr VK_NULL devicePtr &
          onVkFailureThrow "vkCreateDevice failed."
        peek devicePtr
  )
  `mkAcquire`
  \device -> vkDestroyDevice device VK_NULL

newVkSwapchain :: VkDevice -> VkSwapchainCreateInfoKHR -> Acquire VkSwapchainKHR
newVkSwapchain device createInfo =
  (
    withPtr createInfo $ \createInfoPtr ->
      alloca $ \swapchainPtr -> do
        vkCreateSwapchainKHR device createInfoPtr VK_NULL swapchainPtr &
          onVkFailureThrow "vkCreateSwapchainKHR failed."
        peek swapchainPtr
  )
  `mkAcquire`
  \swapchain -> vkDestroySwapchainKHR device swapchain VK_NULL

But now I've encountered vkCreateGraphicsPipelines, which has an unusual signature. Instead of taking a pointer to one CreateInfo data structure, it takes a pointer to an array of them. I can follow the above pattern to use it for creating just one pipeline like this:

newGraphicsPipeline :: VkDevice -> VkGraphicsPipelineCreateInfo -> Acquire VkPipeline
newGraphicsPipeline device createInfo =
  (
    withPtr createInfo $ \createInfoPtr ->
      alloca $ \pipelinePtr -> do
        vkCreateGraphicsPipelines device VK_NULL_HANDLE 1 createInfoPtr VK_NULL pipelinePtr &
          onVkFailureThrow "vkCreateGraphicsPipelines failed."
        peek pipelinePtr
  )
  `mkAcquire`
  \pipeline -> vkDestroyPipeline device pipeline VK_NULL

But if I wanted to make this:

newGraphicsPipelines :: VkDevice -> [VkGraphicsPipelineCreateInfo] -> Acquire [VkPipeline]

I don't see a way to get a pointer to an array of those CreateInfos. Am I missing something, or should we add a withArrayPtr or withListPtr or something?

Functions to safely read from Ptr fields

I managed to shoot myself in the foot by reading back an array that was set via setListCountAndRef by extracting the Ptr near the end of the lifetime of a struct, followed by reading the array with peekArray. Because the array lifetime is coupled to the struct lifetime, this caused the array to be overwritten sometimes before peekArray was finished.
While that's entirely my fault, it would be nice to have more field accessor functions. I already have some in Program.Foreign: withUnsafeField, getListCountAndRef. (Besides, the function withArrayLen from this module should probably be in Graphics.Vulkan.Marshal). Not making a pull request because I don't know where to put them, and I guess they can be done without IO somehow.

Try vulkan on arm 32/64bit or x86 32 bit.

I tested that the library compiles on arm, but I did not manage to run it due to lack of arm devices with vulkan support.

Thanks to hsc2hs, everything should work in theory; so I am curious to see if the library can run on mobile or other 32 bit platforms in reality.
The simplest way to test the minimal functionality is to try ve-01-CreateInstance from vulkan-examples folder.

How to install GLWF?

The readme says "Windowing is done via GLFW, so you may need to have it on your system, version 3.2 or newer."

I'm not sure what exactly is meant by "have it on your system". I'm on Windows and new to Haskell. I suspect I need to download the precompiled binaries (.ddls and .libs) and deposit them somewhere on my system, but I'm not sure where.

Also, in the Lib folder in the examples, Graphics.UI.GLFW is imported. Is this inside the vulkan-api package, or do I need to import this elsewhere?

Vulkan 1.1 support

The Vulkan 1.1 spec was released recently. How much work is it to support that with the bindings?

vulkan-api on macOS should use libvulkan instead of libMoltenVK by default, and ideally the same lib as the one GLFW already loaded

I have tried a lot of things trying to get vulkan-examples (executable ve-06-Drawing) to work with MoltenVK on macOS, but always get the error "ve-06-Drawing: VulkanException {vkeCode = Nothing, vkeMessage = "GLFW reports that vulkan is not supported!"}". It would be awesome if the README had instructions on how to pull that off. I'm assuming this is possible because the README claims that vulkan-api is tested with MoltenVK.

System: macOS 10.14.3
cabal-install and Cabal version: 2.4.1.0
ghc: 8.6.4

Things I tried:

  • cabal flags --extra-include-dirs and --extra-lib-dirs
  • --constraint="bindings-GLFW +system-GLFW" with glfw from homebrew
  • putting the MoltenVK.framework and vulkan.framework in /Library/Frameworks and writing "frameworks: MoltenVK, vulkan" into cabal file
  • DYLD_INSERT_LIBRARIES
  • reading glfw C source code from bindings-GLFW trying to figure out how it detects Vulkan.

I think that static linking to the frameworks doesn't work because the C code is built without cmake which would define _GLFW_VULKAN_STATIC. This define is queried in vulkan.c. Interestingly this file only mentions .dll and .so files as parameters to dlopen, not .dylib. But I'm probably missing something.

New release needed

Currently HEAD works for me with GHC 8.10.4, but the version from Hackage doesn't. The following errors occur with the Hackage version:

src/Graphics/Vulkan/Marshal/Internal.hs:186:17: error:
    • errMsg
    • In the expression:
        error
          "VulkanFields.withField: unreachable code (no such field guarded by type family)."
      In an equation for ‘withField’:
          withField
            = error
                "VulkanFields.withField: unreachable code (no such field guarded by type family)."
      In the instance declaration for ‘VulkanFields '[]’
    |
186 |     withField = error "VulkanFields.withField: unreachable code (no such field guarded by type family)."
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/Graphics/Vulkan/Marshal/Internal.hs:201:28: error:
    • Cannot apply expression of type ‘Proxy# _0’
      to a visible type argument ‘m’
    • In the first argument of ‘k’, namely ‘(proxy# @_ @m)’
      In the first argument of ‘(.)’, namely ‘k (proxy# @_ @m)’
      In the expression: k (proxy# @_ @m) . enumerateFields @ms k
    |
201 |     enumerateFields k = k (proxy# @_ @m) . enumerateFields @ms k
    |                            ^^^^^^^^^^^^

getFieldArray and runtime indices

It occurred to me that, without getVec from easytensor, I don't see how you can index something like the memoryTypes of VkPhysicalDeviceMemoryProperties using an index obtained at runtime. It supports something like getFieldArray @"memoryTypes" @5 pdmp, but how would you index given an Int value?

In the meantime, I'm just using getVec, but it doesn't make sense to require an external library for this.

Logo contribution

I would like to volunteer to design a logo for use in the readme file. What do you think?

VkFormat not an instance of Num

What's the reason for removing this instance? I can work around it by explicitly wrapping/unwrapping a number with VkFormat, but it would be convenient if VkFormat itself were a number.

need example or documentaion for reading from an array with a count field

Admittedly I don't know very much about haskell, so I apologize if the solution is obvious, but I can't seem to figure out how to read from an array which has a variable length.

The best I could come up with is this (for reading the memoryTypes from the "vkGetPhysicalDeviceMemoryProperties" function)

      memTypes <- case memTypeIndex of
        0 -> Just <$> readFieldArray @"memoryTypes" @0 memPropPtr
        1 -> Just <$> readFieldArray @"memoryTypes" @1 memPropPtr
        2 -> Just <$> readFieldArray @"memoryTypes" @2 memPropPtr
        3 -> Just <$> readFieldArray @"memoryTypes" @3 memPropPtr
        4 -> Just <$> readFieldArray @"memoryTypes" @4 memPropPtr
        5 -> Just <$> readFieldArray @"memoryTypes" @5 memPropPtr
        6 -> Just <$> readFieldArray @"memoryTypes" @6 memPropPtr
        7 -> Just <$> readFieldArray @"memoryTypes" @7 memPropPtr
...

I can't find any example or code anywhere that uses readFieldArray with a variable length, so with my limited knowledge of Haskell, I'm not sure if there is a better way.

Would it be possible to describe how this is done in some documentation somewhere or in an example?

Expose Graphics.Vulkan.Marshal.Create.Union?

I'm trying to create some helper functions to eliminate some of the monotony of creating structures, for instance:

applicationInfo ::
  CreateVkStruct VkApplicationInfo
    '[
      "pApplicationName",
      "applicationVersion",
      "pEngineName",
      "engineVersion",
      "apiVersion"
    ] () ->
  VkApplicationInfo
applicationInfo rest =
  createVk $
  set @"sType" VK_STRUCTURE_TYPE_APPLICATION_INFO &*
  set @"pNext" VK_NULL &*
  rest

However, with this implementation, I have to supply the fields in the order specified in the type signature. Is there some way to resolve this? Would exposing the Union type family help?

Also, it seems like it might be more flexible in this context if there were a way to specify "a CreateVkStruct that has all the fields set except these", and then have the type signature list sType and pNext rather than the ones that the caller must provide.

Enum instance of FlagMask-typed bitmasks produces unexpected values

If you write this:

enumFromTo @VkSampleCountFlags VK_SAMPLE_COUNT_1_BIT VK_SAMPLE_COUNT_64_BIT

I would expect it to produce a list like:

[VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT, VK_SAMPLE_COUNT_16_BIT, VK_SAMPLE_COUNT_32_BIT, VK_SAMPLE_COUNT_64_BIT]

But instead it produces this list:

[VK_SAMPLE_COUNT_1_BIT,VK_SAMPLE_COUNT_2_BIT,VkSampleCountBitmask 3,VK_SAMPLE_COUNT_4_BIT,VkSampleCountBitmask 5,VkSampleCountBitmask 6,VkSampleCountBitmask 7,VK_SAMPLE_COUNT_8_BIT,VkSampleCountBitmask 9,VkSampleCountBitmask 10,VkSampleCountBitmask 11,VkSampleCountBitmask 12,VkSampleCountBitmask 13,VkSampleCountBitmask 14,VkSampleCountBitmask 15,VK_SAMPLE_COUNT_16_BIT,VkSampleCountBitmask 17,VkSampleCountBitmask 18,VkSampleCountBitmask 19,VkSampleCountBitmask 20,VkSampleCountBitmask 21,VkSampleCountBitmask 22,VkSampleCountBitmask 23,VkSampleCountBitmask 24,VkSampleCountBitmask 25,VkSampleCountBitmask 26,VkSampleCountBitmask 27,VkSampleCountBitmask 28,VkSampleCountBitmask 29,VkSampleCountBitmask 30,VkSampleCountBitmask 31,VK_SAMPLE_COUNT_32_BIT,VkSampleCountBitmask 33,VkSampleCountBitmask 34,VkSampleCountBitmask 35,VkSampleCountBitmask 36,VkSampleCountBitmask 37,VkSampleCountBitmask 38,VkSampleCountBitmask 39,VkSampleCountBitmask 40,VkSampleCountBitmask 41,VkSampleCountBitmask 42,VkSampleCountBitmask 43,VkSampleCountBitmask 44,VkSampleCountBitmask 45,VkSampleCountBitmask 46,VkSampleCountBitmask 47,VkSampleCountBitmask 48,VkSampleCountBitmask 49,VkSampleCountBitmask 50,VkSampleCountBitmask 51,VkSampleCountBitmask 52,VkSampleCountBitmask 53,VkSampleCountBitmask 54,VkSampleCountBitmask 55,VkSampleCountBitmask 56,VkSampleCountBitmask 57,VkSampleCountBitmask 58,VkSampleCountBitmask 59,VkSampleCountBitmask 60,VkSampleCountBitmask 61,VkSampleCountBitmask 62,VkSampleCountBitmask 63,VK_SAMPLE_COUNT_64_BIT]

Is this a mistake, or are my expectations wrong?

Shard api in vulkan-triangles to dedicated module?

What's the state (fate?) of the Program code in vulkan-triangles? With the exception of a few of the hard-coded device / chain / instance queries, it looks to be a good foundation for a general api. How would you feel about moving it to a dedicated module in vulkan-api or a standalone library one-step in abstraction above vulkan-api?

genvulkan build error

environment
windows 10
vulkan sdk 1.1.92.1

I got an error below.

D:\Work\vulkan\genvulkan>stack exec genvulkan
Parsing file "D:\Work\vulkan\vulkan-docs\xml\vk.xml";
Output folder is "D:\Work\vulkan\vulkan-api\src-gen\";
Warning: ignoring tag.
Done parsing, start generating...
genvulkan.EXE: Write.Types.Define.genDefine: unknown define!
Please, add a new guard to the function. Data: VkTypeSimple {name = VkTypeName {unVkTypeName = "VK_MAKE_VERSION"}, attributes = VkTypeAttrs {name = Nothing, alias = Nothing, category = VkTypeCatDefine, requires = Nothing, parent = [], returnedonly = False, comment = "", structextends = []}, typeData = VkTypeData {name = Just (VkTypeName {unVkTypeName = "VK_MAKE_VERSION"},[]), retType = Nothing, reference = [], comment = Nothing, code = "#define VK_MAKE_VERSION(major, minor, patch) \\r\n (((major) << 22) | ((minor) << 12) | (patch))"}}
CallStack (from HasCallStack):
error, called at src\Write\Types\Define.hs:155:17 in genvulkan-1.1.3.0-BkKDr8oYUStImjbP53zqC6:Write.Types.Define

So, I fixed the code ""#define VK_MAKE_VERSION(major, minor, patch) \\\n" ==> "#define VK_MAKE_VERSION(major, minor, patch) \\\r\n"

inert the "\r".

genvulkan runtime error on windows ( my opinion )

#21

I Just recommend to fix your code like this.

Define.hs 53 line

&& (c == "#define VK_MAKE_VERSION(major, minor, patch) \\n (((major) << 22) | ((minor) << 12) | (patch))" || c == "#define VK_MAKE_VERSION(major, minor, patch) \\r\n (((major) << 22) | ((minor) << 12) | (patch))")

It works on any os.

Too many modules causes windows build crash

vulkan-api has a lot of modules.
The modules build up into a really long command line passed to GHC, which causes Windows build to crash with createProcess: does not exist (No such file or directory) error message.

To workaround this problem, I need to either split the build command into several, or group some modules.

Generalized VkStruct?

Have you considered, or do you see any problem with, the idea of making a generalization of VkStruct, one that's not Vulkan specific? It just seems to me that the ability to read/write a byte array as a struct with named and typed fields would be useful elsewhere. Particularly in C-library bindings.

Help pls

um sry im a little bit new to haskell, mosty us c and rust, but i was wandering what all the '@' operators were or is it a custom operator, and if there were any docs for this library.

Undefined reference to 'vkCreateDebugReportCallbackEXT'

I'm working through https://vulkan-tutorial.com/, and translating the example code to Haskell as I go. My source is here: https://github.com/rotaerk/vulkanTest.

I got as far as adding validation layers, with everything building and running as expected, but when I added the debug callback (https://github.com/Rotaerk/vulkanTest/blob/master/src/Main.hs#L184), I got the following link error:

Linking dist/build/vulkanTest/vulkanTest ...
dist/build/vulkanTest/vulkanTest-tmp/Main.p_o: In function sGDa_info': /tmp/nix-build-vulkanTest-0.1.0.0.drv-0/ghc15641_0/ghc_9.p_o:(.text.sGDa_info+0x87): undefined reference to vkDestroyDebugReportCallbackEXT'
dist/build/vulkanTest/vulkanTest-tmp/Main.p_o: In function cIyh_info': /tmp/nix-build-vulkanTest-0.1.0.0.drv-0/ghc15641_0/ghc_9.p_o:(.text.sGCM_info+0x2cf): undefined reference to vkCreateDebugReportCallbackEXT'
collect2: error: ld returned 1 exit status
cc' failed in phase Linker'. (Exit code: 1)

I'm not sure whether there's a problem with what I'm doing, so this may not be an issue with your bindings. However, I ran across this stackoverflow post indicating that for functions from Vulkan extensions, you're supposed to use vkGetInstanceProcAddr to dynamically load them. I was told that this is true for all extensions.

Your bindings don't do that; instead they do a foreign import of them just like with the core Vulkan functions. I do see that you have some examples using extensions, and I'm not sure how those would work if the above is true ...

Do you have any idea where the problem lies, here?

VK_ERROR_LAYER_NOT_PRESENT

Hi,

I'm trying to run the examples. 01 works, with others I get same error as with 02:

`

stack exec ve-01-CreateInstance
Success! 0x0000000000abffd0
VK_SUCCESS

stack exec ve-02-GLFWWindow
GLFW version: 3.3.2 Win32 WGL EGL OSMesa MinGW
Initialized GLFW window.
Closed GLFW window.
Terminated GLFW.
ve-02-GLFWWindow.EXE: VulkanException {vkeCode = Just VK_ERROR_LAYER_NOT_PRESENT, vkeMessage = "vkCreateInstance: Failed to create vkInstance."}`

System: Windows 8.1
Vuklan SDK installed, the examples are running.

I have built the examples with "stack build", also no modifications were made, except adding the extra dependencies for GLFW as suggested by stack.

I assume this could be the problem with my setup, so I'll be greateful if you could push me in the right direction...

P.S.
When trying to build triangles, I get the following error: (I can't see how they would be connected, so just in case)

E:\temp\vulkan\vulkan-triangles\src\Lib\Vulkan\Vertex.hs:59:22: error:
Not in scope: type constructor or class `KnownDimType'
|
59 | atLeastThree :: (All KnownDimType ns, BoundedDims ns)
| ^^^^^^^^^^^^
Completed 50 action(s).

Conversion between FlagBit and FlagMask variants

I'm not really clear on the intended distinction between these, but I have a function like this that won't typecheck:

getMaxUsableSampleCount :: MonadIO io => VkPhysicalDevice -> io VkSampleCountFlagBits
getMaxUsableSampleCount physicalDevice = do
  limits <- getField @"limits" <$> getPhysicalDeviceProperties physicalDevice
  let sampleCounts = min (getField @"framebufferColorSampleCounts" limits) (getField @"framebufferDepthSampleCounts" limits)
  return $
    fromMaybe VK_SAMPLE_COUNT_1_BIT .
    find ((0 /=) . (sampleCounts .&.)) $
    [
      VK_SAMPLE_COUNT_64_BIT,
      VK_SAMPLE_COUNT_32_BIT,
      VK_SAMPLE_COUNT_16_BIT,
      VK_SAMPLE_COUNT_8_BIT,
      VK_SAMPLE_COUNT_4_BIT,
      VK_SAMPLE_COUNT_2_BIT
    ]

The type works if I replace the return type with VkSampleCountFlags. However, I need FlagBits because that's what's consumed later by the samples field of the VkImageCreateInfo structure. Only way I can see to work around this is something like:

getMaxUsableSampleCount :: MonadIO io => VkPhysicalDevice -> io VkSampleCountFlagBits
getMaxUsableSampleCount physicalDevice = do
  limits <- getField @"limits" <$> getPhysicalDeviceProperties physicalDevice
  let sampleCounts = min (getField @"framebufferColorSampleCounts" limits) (getField @"framebufferDepthSampleCounts" limits)
  return $
    fromMaybe VK_SAMPLE_COUNT_1_BIT .
    fmap snd .
    find ((0 /=) . (sampleCounts .&.) . fst) $
    [
      (VK_SAMPLE_COUNT_64_BIT, VK_SAMPLE_COUNT_64_BIT),
      (VK_SAMPLE_COUNT_32_BIT, VK_SAMPLE_COUNT_32_BIT),
      (VK_SAMPLE_COUNT_16_BIT, VK_SAMPLE_COUNT_16_BIT),
      (VK_SAMPLE_COUNT_8_BIT, VK_SAMPLE_COUNT_8_BIT),
      (VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_4_BIT),
      (VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_2_BIT)
    ]

gltf interop

Hi,

I recently started a library to load assets from the gltf spec (here) with the aim of being able to efficiently get assets and animations to the vulkan api (using this c++ project as a reference).

I wondered if you could provide some advice: If I have a buffer or raw binary vertex data (ie, a Mesh) parsed as a little endian ByteString, some accessors telling me what components this buffer represents, the number of bytes per component, the stride, etc, what's the best way to get that geometry into a form that I can get to vkVertexBuffer efficiently?

I've looked at the DataFrame PrimBytes instance but I'm not exactly sure where to start as the easytensor vulkan-api design goals are not totally clear to me.

Thanks!

Flag-controlled default for safe/unsafe FFI

ATM, all FFI by default is unsafe, which makes writing haskell debug callback troublesome.
The proposal is to add another copy for all exported FFI functions, so that the a vulkan call vkXxx would be translated to three haskell functions:

foreign import ccall safe "vkXxx" vkXxxSafe :: ...
...
foreign import ccall unsafe "vkXxx" vkXxxUnsafe :: ...
...
#ifdef SAFE_FFI_DEFAULT
vkXxx = vkXxxSafe
#else
vkXxx = vkXxxUnsafe
#endif

The same for vk[Get|Lookup][Instance|Device|]Proc.

Another approach would be to add one boolean type parameter to each vkXxx function to define whether to call it safe or unsafe. Though, this would require another type class to allow writing two versions of each function (to keep the choice zero-cost at runtime via specialization-inlining).

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.