expipiplus1 / vulkan

Haskell bindings for Vulkan
https://hackage.haskell.org/package/vulkan
BSD 3-Clause "New" or "Revised" License
138 stars 31 forks source link

use spirv-cross for generate some shader related vkCreateInfo in Vulkan.Util? #260

Open ukari opened 3 years ago

ukari commented 3 years ago

I write a example https://gist.github.com/ukari/6b9d94b04c3c26ff63ffd4663914c7da

and inlined some examples which could easily run by excute test2(for makeShaderInfo), test3(for makeDescriptorInfo), test4(for makeInputInfo)

module SpirV
  ( reflection
  , makeShaderInfo
  , makeDescriptorInfo
  , makeInputInfo
  ) where

import Vulkan.Core10.Shader (ShaderModule)

data Shader = Shader
  { stage :: ShaderStage
  , code :: B.ByteString
  } deriving (Show)

data Reflection = Reflection
  { entryPoints :: Vector EntryPoint
  , inputs :: Maybe (Vector Input)
  , textures :: Maybe (Vector Texture)
  , ubos :: Maybe (Vector Ubo)
  } deriving (Show, Generic, FromJSON, ToJSON)

data ShaderInfo = ShaderInfo
  { shaderModuleCreateInfo:: ShaderModuleCreateInfo '[]
  , pipelineShaderStageCreateInfos :: ShaderModule -> Vector (PipelineShaderStageCreateInfo '[])
  }

reflection :: MonadIO m => ShaderStage -> "code" ::: String -> m (Shader, Reflection)

makeShaderInfo :: (Shader, Reflection) -> ShaderInfo

makeDescriptorInfo :: Vector (Shader, Reflection) -> Vector (DescriptorSetLayoutCreateInfo '[])

makeInputInfo :: Vector (Shader, Reflection) -> Maybe (PipelineVertexInputStateCreateInfo '[])

now this only support single binding for input vertex, which let all input vertex attribute have a default binding = 0.

maybe can provide a binding map parameter to specify individual binding by name, like foo [(1, ["inPos", "inColor"]) (2, ["texCoord"])] speficies that (binding = 1) inPos, (binding = 1) inColor, (binding = 2) texCoord

here is some related references:

SaschaWillems - Multiple Vulkan buffer binding points

island pipeline

sheaf commented 3 years ago

It's definitely worth investigating what information can be extracted from a shader or shader pipeline which would allow for automation of Vulkan operations (such as creation of a graphics pipeline and binding of appropriate resources).

Given that Vulkan is based on SPIR-V, I think it makes the most sense to have something that handles SPIR-V directly and extracts relevant information. Yet it still seems quite difficult as there are a lot of features one needs to take into account: descriptor sets, binding numbers, arrayed descriptors, visibility of descriptors in different stages, different storage classes (uniform buffers, storage buffers, push constants, acceleration structures,...), and so on. I doubt it's really possible to come up with a comprehensive solution that would avoid having to manually handle resource management on the Vulkan side in general. That's what I keep finding with the Vulkan API: you try to come up with some abstractions to simplify your tasks, but then when your requirements change ever so slightly you realise you needed access to some part or other. So at some level I believe there's no bypassing the low-level nature of Vulkan unless you start strongly restricting functionality.

You might be interested in the approach I took here for handling descriptor sets. You specify a bunch of different descriptors and it will go ahead and figure out everything it needs to handle

See for instance this example in which I declare the kind of descriptor sets I want, and then later on the shader stage flags and the data to use for initialisation. The functions from Vulkan.Resource then allocate everything, returning the descriptor set layouts, descriptor sets, and functions that allow you to update the underlying data that the descriptors point to (e.g. to move the camera).

ukari commented 3 years ago

@sheaf Hi, I read a bit of your example. I have some questions but don't know it is suitable to open a issue under FIR so I ask here.

FIR seems directly generate spirv code from AST, why there is a example/CreateDirs which helps to compile shader to a spv file. This weird me a bit. I thought it could use a temporary directory for storage spv file or directly pass spv code to vulkan api. Or maybe this is just for better generated spirv debug experience?

Thy way FIR use to write shader is a eDSL -> spv approach. It would be more nice if there could be a vulkan glsl -> eDSL or spv -> eDSL because people mostly teach shader in glsl or hlsl. Write spriv with eDSL is a little difficult.

sheaf commented 3 years ago

The data directory thing is just a way of writing the shader files to disk in a portable way. I'm using it to implement live shader reloading (by watching the files change on disk), and it's also useful to have the SPIR-V on disk so that you can run the various SPIR-V tools on it. However it's also possible to directly pass the SPIR-V data to Vulkan without writing to disk (personally I find it more convenient to have them on disk).

I wrote the FIR library specifically to avoid having to write GLSL (and in fact I started the project when I realised I could bypass GLSL entirely by compiling directly to SPIR-V), but I understand that it's not for everyone.

expipiplus1 commented 3 years ago

Thanks!

In general this kind of stuff seems like a really good fit for vulkan-utils.

with the Vulkan API: you try to come up with some abstractions to simplify your tasks, but then when your requirements change ever so slightly you realise you needed access to some part or other

This is certainly true, but as long as there's some "escape hatch" then covering the simple cases is very handy.

Whatever ends up in utils would have to have some good documentation and an example due to the trickyness of this in vulkan.

dpwiz commented 3 years ago

There can be a package that has only the base types for SPIR-V derived data (and maybe code generation build-tools).

Shader-producing packages (vulkan-utils, FIR, shader libraries) then can provide types and values to shader-consuming packages (vulkan examples, frameworks and projects) with compile-time compatibility checks. There is jQuery on hackage, why not bloom postprocessing code?

Same energy: https://github.com/embarkstudios/rust-gpu#why-embark

ukari commented 3 years ago

@dpwiz What is a SPIR-V derived data? Does it means types for the json of spirv-cross reflection?

dpwiz commented 3 years ago

@dpwiz What is a SPIR-V derived data? Does it means types for the json of spirv-cross reflection?

Yes, the reflection data.

ukari commented 3 years ago

Is anyone works on this? I am not sure how to contribute it due to the example I wrote only support the few part of reflection data now.

Should I put the WIP stuff into Vulkan.Utils module though the feature is still weak? Or just put it into the examples?

dpwiz commented 3 years ago

I'd suggest sending a PR with the code packaged as vulkan-reflect. Maybe there can be a part that works without even depending on the big vulkan package.

ukari commented 3 years ago

note

The api in current example

makeDescriptorInfo :: Vector (Shader, Reflection) -> Vector (DescriptorSetLayoutCreateInfo '[])
--                            ^ not rely `vulkan`            ^ rely on `vulkan`
--                                    ^ not rely `vulkan`

And in this reflect package(in imagination), the api could be like

data DescriptorSetLayout = ...
--   ^ not rely `vulkan`
makeDescriptorInfo :: Vector (Shader, Reflection) -> (DescriptorSetLayout -> a) -> Vector a
--                            ^ not rely `vulkan`
--                                    ^ not rely `vulkan`
ukari commented 3 years ago

@dpwiz I noticed that FIR examples also use stuffs like PipelineShaderStageCreateInfo from vulkan package. Is it worth to abstract a part which not rely on vulkan? There seems no more active projects(except fir and vulkan) about vulkan in haskell

dpwiz commented 3 years ago

I imagine some CLI or web tool that can be used to inspect shader binaries and maybe generate code. They may not need to actually instantiate Vulkan data or use any libvulkan code.

But things like "just give me an appropriate CreateInfo" can be a great addition to vulkan-utils.

ukari commented 3 years ago

I make a vulkan-reflect branch locally https://github.com/ukari/vulkan/tree/vulkan-reflect

some stuffs might need to be done

dpwiz commented 3 years ago

You can start a PR so we can post comments attached to actual code.

I may be wrong, but it seems you decode from Aeson's Text into String type to fields only to use fromString function via type class. You can add FromJSON instances directly to types like ShaderStage and use Aeson.withText wrappers to decode its values right away.

data EntryPoint = EntryPoint
  { name :: Text
  , mode :: ShaderStage
  } deriving (Show, Generic, FromJSON, ToJSON)
dpwiz commented 2 years ago

BTW, there are now 2 packages to query spirv-reflect instead of spirv-cross

I haven't tried to generate haskell types, but it contains a bit more information about types and their layouts.