Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose Graphics.Vulkan.Marshal.Create.Union? #13

Open
Rotaerk opened this issue Sep 7, 2018 · 7 comments
Open

Expose Graphics.Vulkan.Marshal.Create.Union? #13

Rotaerk opened this issue Sep 7, 2018 · 7 comments

Comments

@Rotaerk
Copy link
Contributor

Rotaerk commented Sep 7, 2018

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.

@achirkin
Copy link
Owner

achirkin commented Sep 7, 2018

I did not expose Union in current implementation, because it's ugly. The whole Create interface is not really mature yet, so I've tried to keep internals hidden to be able to rewrite it later.

Maybe another solution would be to add an automatic sorting of fields before comparing them?.. Let's keep this issue open, I want to explore the options. [I am about to finish my thesis now, but I want to work on the bindings after that]

@Rotaerk
Copy link
Contributor Author

Rotaerk commented Sep 7, 2018

Ah nice; congrats!

@Rotaerk
Copy link
Contributor Author

Rotaerk commented Sep 24, 2019

Ran into an abstraction limitation related to this. I currently have this helper function:

setSharingQueueFamilyIndices ::
  (
    CanWriteField "sharingMode" a,
    CanWriteField "queueFamilyIndexCount" a,
    CanWriteField "pQueueFamilyIndices" a,
    FieldType "sharingMode" a ~ VkSharingMode,
    FieldType "queueFamilyIndexCount" a ~ Word32,
    FieldType "pQueueFamilyIndices" a ~ Ptr Word32
  ) =>
  [Word32] ->
  CreateVkStruct a '["sharingMode", "queueFamilyIndexCount", "pQueueFamilyIndices"] ()
setSharingQueueFamilyIndices qfis =
  set @"sharingMode" (if null qfis' then VK_SHARING_MODE_EXCLUSIVE else VK_SHARING_MODE_CONCURRENT) &*
  setListCountAndRef @"queueFamilyIndexCount" @"pQueueFamilyIndices" qfis'
  where
    -- If just one QFI is provided, it's the same as providing none; both are exclusive mode,
    -- and the QFI list is ignored in that case.
    qfis' = if length qfis > 1 then qfis else []

I then noticed that different structs have these fields named differently. For example, while VkBufferCreateInfo has sharingMode, VkSwapchainCreateInfo has imageSharingMode. So, I tried to make a generalization of the above function:

setSharingQFIs ::
  forall (sm :: GHC.Symbol) (qfic :: GHC.Symbol) (pqfis :: GHC.Symbol) a.
  (
    CanWriteField sm a,
    CanWriteField qfic a,
    CanWriteField pqfis a,
    FieldType sm a ~ VkSharingMode,
    FieldType qfic a ~ Word32,
    FieldType pqfis a ~ Ptr Word32
  ) =>
  [Word32] ->
  CreateVkStruct a '[sm, qfic, pqfis] ()
setSharingQFIs qfis =
  set @sm (if null qfis' then VK_SHARING_MODE_EXCLUSIVE else VK_SHARING_MODE_CONCURRENT) &*
  setListCountAndRef @qfic @pqfis qfis'
  where
    -- If just one QFI is provided, it's the same as providing none; both are exclusive mode,
    -- and the QFI list is ignored in that case.
    qfis' = if length qfis > 1 then qfis else []

Unfortunately, this gives the following error:

    • Could not deduce: Graphics.Vulkan.Marshal.Create.Union
                          (Graphics.Vulkan.Marshal.Internal.VkStruct
                             (Graphics.Vulkan.Marshal.Internal.VkStruct' a))
                          '[sm]
                          (Graphics.Vulkan.Marshal.Create.Union
                             (Graphics.Vulkan.Marshal.Internal.VkStruct
                                (Graphics.Vulkan.Marshal.Internal.VkStruct' a))
                             '[qfic]
                             '[pqfis])
                        ~ '[sm, qfic, pqfis]
      from the context: (CanWriteField sm a, CanWriteField qfic a,
                         CanWriteField pqfis a, FieldType sm a ~ VkSharingMode,
                         FieldType qfic a ~ Word32, FieldType pqfis a ~ Ptr Word32)
        bound by the type signature for:
                   setSharingQFIs :: forall (sm :: GHC.Symbol) (qfic :: GHC.Symbol) (pqfis :: GHC.Symbol) a.
                                     (CanWriteField sm a, CanWriteField qfic a,
                                      CanWriteField pqfis a, FieldType sm a ~ VkSharingMode,
                                      FieldType qfic a ~ Word32, FieldType pqfis a ~ Ptr Word32) =>
                                     [Word32] -> CreateVkStruct a '[sm, qfic, pqfis] ()
        at src/Main.hs:(599,1)-(610,40)
      Expected type: CreateVkStruct a '[sm, qfic, pqfis] ()
        Actual type: CreateVkStruct
                       a
                       (Graphics.Vulkan.Marshal.Create.Union
                          a
                          '[sm]
                          (Graphics.Vulkan.Marshal.Create.Union
                             (Graphics.Vulkan.Marshal.Internal.VkStruct
                                (Graphics.Vulkan.Marshal.Internal.VkStruct' a))
                             '[qfic]

I suspect this is because it cannot prove that sm, qfic, and pqfis are all different. The only way I can think of around this would be to explicitly add the constraint that it can't prove, though I can't do that if Union isn't exported.

@achirkin
Copy link
Owner

Hm, yes, you are right, I have to expose Union.
In the meanwhile, if you feel lucky, you can just unsafeCoerce the result :) Nothing bad should happen since you would be in fact coercing a single type type equality.

@Rotaerk
Copy link
Contributor Author

Rotaerk commented Sep 24, 2019

I wonder if the Create stuff could be redesigned such that the requirement that all the expected fields be set exactly once is enforced later than it is now... For example, in my setSharingQFIs function, there's no need for the type system to enforce that a given field isn't set more than once. That need only be enforced when it's actually passed to createVk or whatever.

In other words, CreateVkStruct a '["foo", "foo", "foo"] () could be valid, but incompatible with createVk. This would make it easier to write functions like the one above.

@achirkin
Copy link
Owner

So, you mean all writing functions would just append the list of written fields and the check for duplicates happens only in the final function createVk :: IsUnion a xs => CreateVkStruct a xs -> a? Good idea! I think we can go with that.

@Rotaerk
Copy link
Contributor Author

Rotaerk commented Sep 24, 2019

Yeah, exactly. BTW, the name Union made sense for the existing data structure, but for a constraint on a list, would IsSet or IsDistinct or something make more sense? Also, is such a constraint event implementable? I'm not sure how, though I'm not that good with the type-level voodoo you do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants