From 1bcc437a37d80e3ad0e6f1dbd5ba858f933a0846 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 26 Oct 2020 20:48:49 +0100 Subject: Add Vulkan backends --- include/pugl/detail/mac_vulkan.m | 216 +++++++++++++++++++++++++++++++++++++++ include/pugl/detail/win_vulkan.c | 130 +++++++++++++++++++++++ include/pugl/detail/x11_vulkan.c | 134 ++++++++++++++++++++++++ include/pugl/pugl_vulkan.h | 148 +++++++++++++++++++++++++++ include/pugl/pugl_vulkan.hpp | 170 ++++++++++++++++++++++++++++++ 5 files changed, 798 insertions(+) create mode 100644 include/pugl/detail/mac_vulkan.m create mode 100644 include/pugl/detail/win_vulkan.c create mode 100644 include/pugl/detail/x11_vulkan.c create mode 100644 include/pugl/pugl_vulkan.h create mode 100644 include/pugl/pugl_vulkan.hpp (limited to 'include/pugl') diff --git a/include/pugl/detail/mac_vulkan.m b/include/pugl/detail/mac_vulkan.m new file mode 100644 index 0000000..813e0ae --- /dev/null +++ b/include/pugl/detail/mac_vulkan.m @@ -0,0 +1,216 @@ +/* + Copyright 2012-2020 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file mac_vulkan.m Vulkan graphics backend for MacOS. +*/ + +#define VK_NO_PROTOTYPES 1 + +#include "pugl/detail/implementation.h" +#include "pugl/detail/mac.h" +#include "pugl/detail/stub.h" +#include "pugl/detail/types.h" +#include "pugl/pugl.h" +#include "pugl/pugl_stub.h" +#include "pugl/pugl_vulkan.h" + +#include +#include + +#import +#import + +#include + +#include +#include + +@interface PuglVulkanView : NSView + +@end + +@implementation PuglVulkanView +{ +@public + PuglView* puglview; +} + +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + self.wantsLayer = YES; + self.layerContentsRedrawPolicy = + NSViewLayerContentsRedrawOnSetNeedsDisplay; + } + + return self; +} + +- (CALayer*)makeBackingLayer +{ + CAMetalLayer* layer = [CAMetalLayer layer]; + [layer setDelegate:self]; + return layer; +} + +- (void)setFrameSize:(NSSize)newSize +{ + PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; + + [super setFrameSize:newSize]; + [wrapper setReshaped]; + + self.layer.frame = self.bounds; +} + +- (void)displayLayer:(CALayer*)layer +{ + (void)layer; + PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; + [wrapper dispatchExpose:[self bounds]]; +} + +@end + +static PuglStatus +puglMacVulkanCreate(PuglView* view) +{ + PuglInternals* impl = view->impl; + PuglVulkanView* drawView = [PuglVulkanView alloc]; + const NSRect rect = NSMakeRect(0, 0, view->frame.width, view->frame.height); + + drawView->puglview = view; + [drawView initWithFrame:rect]; + if (view->hints[PUGL_RESIZABLE]) { + [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + } else { + [drawView setAutoresizingMask:NSViewNotSizable]; + } + + impl->drawView = drawView; + return PUGL_SUCCESS; +} + +static PuglStatus +puglMacVulkanDestroy(PuglView* view) +{ + PuglVulkanView* const drawView = (PuglVulkanView*)view->impl->drawView; + + [drawView removeFromSuperview]; + [drawView release]; + + view->impl->drawView = nil; + return PUGL_SUCCESS; +} + +struct PuglVulkanLoaderImpl { + void* libvulkan; + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; +}; + +PuglVulkanLoader* +puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world)) +{ + PuglVulkanLoader* loader = (PuglVulkanLoader*) + calloc(1, sizeof(PuglVulkanLoader)); + if (!loader) { + return NULL; + } + + if (!(loader->libvulkan = dlopen("libvulkan.dylib", RTLD_LAZY))) { + free(loader); + return NULL; + } + + loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) + dlsym(loader->libvulkan, "vkGetInstanceProcAddr"); + + loader->vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr) + dlsym(loader->libvulkan, "vkGetDeviceProcAddr"); + + return loader; +} + +void +puglFreeVulkanLoader(PuglVulkanLoader* loader) +{ + if (loader) { + dlclose(loader->libvulkan); + free(loader); + } +} + +PFN_vkGetInstanceProcAddr +puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader) +{ + return loader->vkGetInstanceProcAddr; +} + +PFN_vkGetDeviceProcAddr +puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader) +{ + return loader->vkGetDeviceProcAddr; +} + +const PuglBackend* +puglVulkanBackend(void) +{ + static const PuglBackend backend = {puglStubConfigure, + puglMacVulkanCreate, + puglMacVulkanDestroy, + puglStubEnter, + puglStubLeave, + puglStubGetContext}; + + return &backend; +} + +const char* const* +puglGetInstanceExtensions(uint32_t* const count) +{ + static const char* const extensions[] = {"VK_KHR_surface", + "VK_MVK_macos_surface"}; + + *count = 2; + return extensions; +} + +VkResult +puglCreateSurface(const PuglVulkanLoader* const loader, + PuglView* const view, + VkInstance instance, + const VkAllocationCallbacks* const allocator, + VkSurfaceKHR* const surface) +{ + PuglInternals* const impl = view->impl; + + PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK = + (PFN_vkCreateMacOSSurfaceMVK)puglGetInstanceProcAddrFunc( + loader)(instance, "vkCreateMacOSSurfaceMVK"); + + const VkMacOSSurfaceCreateInfoMVK info = { + VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, + NULL, + 0, + impl->drawView, + }; + + return vkCreateMacOSSurfaceMVK(instance, &info, allocator, surface); +} diff --git a/include/pugl/detail/win_vulkan.c b/include/pugl/detail/win_vulkan.c new file mode 100644 index 0000000..cfe3613 --- /dev/null +++ b/include/pugl/detail/win_vulkan.c @@ -0,0 +1,130 @@ +/* + Copyright 2012-2020 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file win_vulkan.c Vulkan graphics backend for Windows. +*/ + +#define VK_NO_PROTOTYPES 1 + +#include "pugl/detail/stub.h" +#include "pugl/detail/types.h" +#include "pugl/detail/win.h" +#include "pugl/pugl_stub.h" +#include "pugl/pugl_vulkan.h" + +#include +#include + +#include + +struct PuglVulkanLoaderImpl +{ + HMODULE libvulkan; + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; +}; + +PuglVulkanLoader* +puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world)) +{ + PuglVulkanLoader* loader = + (PuglVulkanLoader*)calloc(1, sizeof(PuglVulkanLoader)); + if (!loader) { + return NULL; + } + + if (!(loader->libvulkan = LoadLibrary("vulkan-1.dll"))) { + free(loader); + return NULL; + } + + loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)GetProcAddress( + loader->libvulkan, "vkGetInstanceProcAddr"); + + loader->vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)GetProcAddress( + loader->libvulkan, "vkGetDeviceProcAddr"); + + return loader; +} + +void +puglFreeVulkanLoader(PuglVulkanLoader* loader) +{ + if (loader) { + FreeLibrary(loader->libvulkan); + free(loader); + } +} + +PFN_vkGetInstanceProcAddr +puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader) +{ + return loader->vkGetInstanceProcAddr; +} + +PFN_vkGetDeviceProcAddr +puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader) +{ + return loader->vkGetDeviceProcAddr; +} + +const PuglBackend* +puglVulkanBackend() +{ + static const PuglBackend backend = {puglWinStubConfigure, + puglStubCreate, + puglStubDestroy, + puglWinStubEnter, + puglWinStubLeave, + puglStubGetContext}; + + return &backend; +} + +const char* const* +puglGetInstanceExtensions(uint32_t* const count) +{ + static const char* const extensions[] = {"VK_KHR_surface", + "VK_KHR_win32_surface"}; + + *count = 2; + return extensions; +} + +VkResult +puglCreateSurface(const PuglVulkanLoader* const loader, + PuglView* const view, + VkInstance instance, + const VkAllocationCallbacks* const pAllocator, + VkSurfaceKHR* const pSurface) +{ + PuglInternals* const impl = view->impl; + + PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = + (PFN_vkCreateWin32SurfaceKHR)puglGetInstanceProcAddrFunc(loader)( + instance, "vkCreateWin32SurfaceKHR"); + + const VkWin32SurfaceCreateInfoKHR createInfo = { + VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, + NULL, + 0, + GetModuleHandle(NULL), + impl->hwnd, + }; + + return vkCreateWin32SurfaceKHR(instance, &createInfo, pAllocator, pSurface); +} diff --git a/include/pugl/detail/x11_vulkan.c b/include/pugl/detail/x11_vulkan.c new file mode 100644 index 0000000..0bd1532 --- /dev/null +++ b/include/pugl/detail/x11_vulkan.c @@ -0,0 +1,134 @@ +/* + Copyright 2012-2020 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file x11_vulkan.c Vulkan graphics backend for X11. +*/ + +#define VK_NO_PROTOTYPES 1 + +#include "pugl/detail/stub.h" +#include "pugl/detail/types.h" +#include "pugl/detail/x11.h" +#include "pugl/pugl.h" +#include "pugl/pugl_vulkan.h" + +#include +#include + +#include + +#include +#include + +struct PuglVulkanLoaderImpl +{ + void* libvulkan; + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; +}; + +PuglVulkanLoader* +puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world)) +{ + PuglVulkanLoader* loader = + (PuglVulkanLoader*)calloc(1, sizeof(PuglVulkanLoader)); + if (!loader) { + return NULL; + } + + if (!(loader->libvulkan = dlopen("libvulkan.so", RTLD_LAZY))) { + free(loader); + return NULL; + } + + loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym( + loader->libvulkan, "vkGetInstanceProcAddr"); + + loader->vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)dlsym( + loader->libvulkan, "vkGetDeviceProcAddr"); + + return loader; +} + +void +puglFreeVulkanLoader(PuglVulkanLoader* loader) +{ + if (loader) { + dlclose(loader->libvulkan); + free(loader); + } +} + +PFN_vkGetInstanceProcAddr +puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader) +{ + return loader->vkGetInstanceProcAddr; +} + +PFN_vkGetDeviceProcAddr +puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader) +{ + return loader->vkGetDeviceProcAddr; +} + +const PuglBackend* +puglVulkanBackend(void) +{ + static const PuglBackend backend = {puglX11StubConfigure, + puglStubCreate, + puglStubDestroy, + puglStubEnter, + puglStubLeave, + puglStubGetContext}; + + return &backend; +} + +const char* const* +puglGetInstanceExtensions(uint32_t* const count) +{ + static const char* const extensions[] = {"VK_KHR_surface", + "VK_KHR_xlib_surface"}; + + *count = 2; + return extensions; +} + +VkResult +puglCreateSurface(const PuglVulkanLoader* const loader, + PuglView* const view, + VkInstance instance, + const VkAllocationCallbacks* const allocator, + VkSurfaceKHR* const surface) +{ + PuglInternals* const impl = view->impl; + PuglWorldInternals* world_impl = view->world->impl; + + PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR = + (PFN_vkCreateXlibSurfaceKHR)puglGetInstanceProcAddrFunc(loader)( + instance, "vkCreateXlibSurfaceKHR"); + + const VkXlibSurfaceCreateInfoKHR info = { + VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, + NULL, + 0, + world_impl->display, + impl->win, + }; + + return vkCreateXlibSurfaceKHR(instance, &info, allocator, surface); +} diff --git a/include/pugl/pugl_vulkan.h b/include/pugl/pugl_vulkan.h new file mode 100644 index 0000000..451c28f --- /dev/null +++ b/include/pugl/pugl_vulkan.h @@ -0,0 +1,148 @@ +/* + Copyright 2012-2020 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_vulkan.h Vulkan-specific API. + + Note that this header includes Vulkan headers, so if you are writing a + program or plugin that dynamically loads vulkan, you should first define + `VK_NO_PROTOTYPES` before including it. +*/ + +#ifndef PUGL_PUGL_VULKAN_H +#define PUGL_PUGL_VULKAN_H + +#include "pugl/pugl.h" + +#include + +PUGL_BEGIN_DECLS + +/** + @defgroup vulkan Vulkan + Vulkan graphics support. + + Vulkan support differs from OpenGL because almost all most configuration is + done using the Vulkan API itself, rather than by setting view hints to + configure the context. Pugl only provides a minimal loader for loading the + Vulkan library, and a portable function to create a Vulkan surface for a + view, which hides the platform-specific implementation details. + + @ingroup pugl_c + @{ +*/ + +/** + Dynamic Vulkan loader. + + This can be used to dynamically load the Vulkan library. Applications or + plugins should not link against the Vulkan library, but instead use this at + runtime. This ensures that things will work on as many systems as possible, + and allows errors to be handled gracefully. + + This is not a "loader" in the sense of loading all the required Vulkan + functions (which is the application's responsibility), but just a minimal + implementation to portably load the Vulkan library and get the two functions + that are used to load everything else. + + Note that this owns the loaded Vulkan library, so it must outlive all use of + the Vulkan API. + + @see https://www.khronos.org/registry/vulkan/specs/1.0/html/chap4.html +*/ +typedef struct PuglVulkanLoaderImpl PuglVulkanLoader; + +/** + Create a new dynamic loader for Vulkan functions. + + This dynamically loads the Vulkan library and gets the load functions from it. + + @return A new Vulkan loader, or null on failure. +*/ +PUGL_API PuglVulkanLoader* +puglNewVulkanLoader(PuglWorld* world); + +/** + Free a loader created with puglNewVulkanLoader(). + + Note that this closes the Vulkan library, so no Vulkan objects or API may be + used after this is called. +*/ +PUGL_API void +puglFreeVulkanLoader(PuglVulkanLoader* loader); + +/** + Return the `vkGetInstanceProcAddr` function. + + @return Null if the Vulkan library does not contain this function (which is + unlikely and indicates a broken system). +*/ +PUGL_API PFN_vkGetInstanceProcAddr +puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader); + +/** + Return the `vkGetDeviceProcAddr` function. + + @return Null if the Vulkan library does not contain this function (which is + unlikely and indicates a broken system). +*/ +PUGL_API PFN_vkGetDeviceProcAddr +puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader); + +/** + Return the Vulkan instance extensions required to draw to a PuglView. + + This simply returns static strings, it does not access Vulkan or the window + system. The returned array always contains at least "VK_KHR_surface". + + @param[out] count The number of extensions in the returned array. + @return An array of extension name strings. +*/ +PUGL_API const char* const* +puglGetInstanceExtensions(uint32_t* count); + +/** + Create a Vulkan surface for a Pugl view. + + @param loader The loader for Vulkan functions. + @param view The view the surface is to be displayed on. + @param instance The Vulkan instance. + @param allocator Vulkan allocation callbacks, may be NULL. + @param[out] surface Pointed to a newly created Vulkan surface. + @return `VK_SUCCESS` on success, or a Vulkan error code. +*/ +PUGL_API VkResult +puglCreateSurface(const PuglVulkanLoader* loader, + PuglView* view, + VkInstance instance, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface); + +/** + Vulkan graphics backend. + + Pass the returned value to puglSetBackend() to draw to a view with Vulkan. +*/ +PUGL_API PUGL_CONST_FUNC const PuglBackend* +puglVulkanBackend(void); + +/** + @} +*/ + +PUGL_END_DECLS + +#endif // PUGL_PUGL_VULKAN_H diff --git a/include/pugl/pugl_vulkan.hpp b/include/pugl/pugl_vulkan.hpp new file mode 100644 index 0000000..9241aba --- /dev/null +++ b/include/pugl/pugl_vulkan.hpp @@ -0,0 +1,170 @@ +/* + Copyright 2012-2020 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_vulkan.hpp Vulkan-specific C++ API. + + Note that this header includes Vulkan headers, so if you are writing a + program or plugin that dynamically loads vulkan, you should first define + `VK_NO_PROTOTYPES` before including it. +*/ + +#ifndef PUGL_PUGL_VULKAN_HPP +#define PUGL_PUGL_VULKAN_HPP + +#include "pugl/pugl.h" +#include "pugl/pugl.hpp" +#include "pugl/pugl_vulkan.h" + +#include + +#include + +namespace pugl { + +/** + @defgroup vulkanxx Vulkan + Vulkan graphics support. + + Note that the Pugl C++ wrapper does not use vulkan.hpp because it is a + heavyweight dependency which not everyone uses, and its design is not very + friendly to dynamic loading in plugins anyway. However, if you do use + vulkan.hpp smart handles, it is relatively straightforward to wrap the + result of createSurface() manually. + + @ingroup pugl_cxx + @{ +*/ + +/// @copydoc PuglVulkanLoader +class PUGL_API VulkanLoader final + : public detail::Wrapper +{ +public: + /** + Create a new dynamic loader for Vulkan functions. + + This dynamically loads the Vulkan library and gets the load functions + from it. + + Note that this constructor does not throw exceptions, though failure is + possible. To check if the Vulkan library failed to load, test this + loader, which is explicitly convertible to `bool`. It is safe to use a + failed loader, but the accessors will always return null. + */ + explicit VulkanLoader(World& world) noexcept + : Wrapper{puglNewVulkanLoader(world.cobj())} + {} + + /** + Return the `vkGetInstanceProcAddr` function. + + @return Null if the Vulkan library failed to load, or does not contain + this function (which is unlikely and indicates a broken system). + */ + PFN_vkGetInstanceProcAddr getInstanceProcAddrFunc() const noexcept + { + return cobj() ? puglGetInstanceProcAddrFunc(cobj()) : nullptr; + } + + /** + Return the `vkGetDeviceProcAddr` function. + + @return Null if the Vulkan library failed to load, or does not contain + this function (which is unlikely and indicates a broken system). + */ + PFN_vkGetDeviceProcAddr getDeviceProcAddrFunc() const noexcept + { + return cobj() ? puglGetDeviceProcAddrFunc(cobj()) : nullptr; + } + + /// Return true if this loader is valid to use + explicit operator bool() const noexcept { return cobj(); } +}; + +/** + A simple wrapper for an array of static C strings. + + This provides a minimal API that supports iteration, like `std::vector`, but + avoids allocation, exceptions, and a dependency on the C++ standard library. +*/ +class PUGL_API StaticStringArray final +{ +public: + using value_type = const char*; + using const_iterator = const char* const*; + using size_type = uint32_t; + + StaticStringArray(const char* const* strings, const uint32_t size) noexcept + : _strings{strings} + , _size{size} + {} + + const char* const* begin() const noexcept { return _strings; } + const char* const* end() const noexcept { return _strings + _size; } + const char* const* data() const noexcept { return _strings; } + uint32_t size() const noexcept { return _size; } + +private: + const char* const* _strings; + uint32_t _size; +}; + +/** + Return the Vulkan instance extensions required to draw to a PuglView. + + If successful, the returned array always contains "VK_KHR_surface", along + with whatever other platform-specific extensions are required. + + @return An array of extension name strings. +*/ +inline StaticStringArray +getInstanceExtensions() noexcept +{ + uint32_t count = 0; + const char* const* const extensions = puglGetInstanceExtensions(&count); + + return StaticStringArray{extensions, count}; +} + +/// @copydoc puglCreateSurface +inline VkResult +createSurface(const VulkanLoader& loader, + View& view, + VkInstance instance, + const VkAllocationCallbacks* const allocator, + VkSurfaceKHR* const surface) noexcept +{ + const VkResult r = puglCreateSurface( + loader.cobj(), view.cobj(), instance, allocator, surface); + + return (!r && !surface) ? VK_ERROR_INITIALIZATION_FAILED : r; +} + +/// @copydoc puglVulkanBackend +static inline const PuglBackend* +vulkanBackend() noexcept +{ + return puglVulkanBackend(); +} + +/** + @} +*/ + +} // namespace pugl + +#endif // PUGL_PUGL_VULKAN_HPP -- cgit v1.2.1